blob: cf395fb38f6b7b14abecbfa75fce3de24330407d [file] [log] [blame]
Ned Deilye1c9794952013-01-29 00:07:46 -08001#!/usr/bin/env python
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00002"""
Ned Deily53c460d2011-01-30 01:43:40 +00003This script is used to build "official" universal installers on Mac OS X.
Ned Deilye1c9794952013-01-29 00:07:46 -08004It requires at least Mac OS X 10.5, Xcode 3, and the 10.4u SDK for
Ned Deily53c460d2011-01-30 01:43:40 +0000532-bit builds. 64-bit or four-way universal builds require at least
6OS X 10.5 and the 10.5 SDK.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00007
Ned Deily53c460d2011-01-30 01:43:40 +00008Please ensure that this script keeps working with Python 2.5, to avoid
9bootstrap issues (/usr/bin/python is Python 2.5 on OSX 10.5). Sphinx,
10which is used to build the documentation, currently requires at least
Ned Deilyebd63dc2014-04-09 16:12:11 -070011Python 2.4. However, as of Python 3.4.1, Doc builds require an external
12sphinx-build and the current versions of Sphinx now require at least
13Python 2.6.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000014
Ned Deilye1c9794952013-01-29 00:07:46 -080015In addition to what is supplied with OS X 10.5+ and Xcode 3+, the script
Ned Deily123a58b2017-07-24 04:29:32 -040016requires an installed third-party version of
Ned Deilye1c9794952013-01-29 00:07:46 -080017Tcl/Tk 8.4 (for OS X 10.4 and 10.5 deployment targets) or Tcl/TK 8.5
18(for 10.6 or later) installed in /Library/Frameworks. When installed,
19the Python built by this script will attempt to dynamically link first to
20Tcl and Tk frameworks in /Library/Frameworks if available otherwise fall
21back to the ones in /System/Library/Framework. For the build, we recommend
22installing the most recent ActiveTcl 8.4 or 8.5 version.
23
2432-bit-only installer builds are still possible on OS X 10.4 with Xcode 2.5
25and the installation of additional components, such as a newer Python
Ned Deily123a58b2017-07-24 04:29:32 -040026(2.5 is needed for Python parser updates) and for the documentation
Ned Deilyebd63dc2014-04-09 16:12:11 -070027build either svn (pre-3.4.1) or sphinx-build (3.4.1 and later).
Ned Deilye1c9794952013-01-29 00:07:46 -080028
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000029Usage: see USAGE variable in the script.
30"""
Ned Deilye1c9794952013-01-29 00:07:46 -080031import platform, os, sys, getopt, textwrap, shutil, stat, time, pwd, grp
32try:
33 import urllib2 as urllib_request
34except ImportError:
35 import urllib.request as urllib_request
36
37STAT_0o755 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
38 | stat.S_IRGRP | stat.S_IXGRP
39 | stat.S_IROTH | stat.S_IXOTH )
40
41STAT_0o775 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
42 | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP
43 | stat.S_IROTH | stat.S_IXOTH )
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000044
Ronald Oussoren158ad592006-11-07 16:00:34 +000045INCLUDE_TIMESTAMP = 1
46VERBOSE = 1
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000047
48from plistlib import Plist
49
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000050try:
51 from plistlib import writePlist
52except ImportError:
53 # We're run using python2.3
54 def writePlist(plist, path):
55 plist.write(path)
56
57def shellQuote(value):
58 """
Ronald Oussorenaa560962006-11-07 15:53:38 +000059 Return the string value in a form that can safely be inserted into
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000060 a shell command.
61 """
62 return "'%s'"%(value.replace("'", "'\"'\"'"))
63
64def grepValue(fn, variable):
Ned Deily62a86602014-12-09 23:45:13 -080065 """
66 Return the unquoted value of a variable from a file..
67 QUOTED_VALUE='quotes' -> str('quotes')
68 UNQUOTED_VALUE=noquotes -> str('noquotes')
69 """
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000070 variable = variable + '='
71 for ln in open(fn, 'r'):
72 if ln.startswith(variable):
73 value = ln[len(variable):].strip()
Ned Deily62a86602014-12-09 23:45:13 -080074 return value.strip("\"'")
Ned Deilye1c9794952013-01-29 00:07:46 -080075 raise RuntimeError("Cannot find variable %s" % variable[:-1])
76
77_cache_getVersion = None
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000078
79def getVersion():
Ned Deilye1c9794952013-01-29 00:07:46 -080080 global _cache_getVersion
81 if _cache_getVersion is None:
82 _cache_getVersion = grepValue(
83 os.path.join(SRCDIR, 'configure'), 'PACKAGE_VERSION')
84 return _cache_getVersion
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000085
Ned Deilye1c9794952013-01-29 00:07:46 -080086def getVersionMajorMinor():
87 return tuple([int(n) for n in getVersion().split('.', 2)])
88
89_cache_getFullVersion = None
90
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000091def getFullVersion():
Ned Deilye1c9794952013-01-29 00:07:46 -080092 global _cache_getFullVersion
93 if _cache_getFullVersion is not None:
94 return _cache_getFullVersion
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +000095 fn = os.path.join(SRCDIR, 'Include', 'patchlevel.h')
96 for ln in open(fn):
97 if 'PY_VERSION' in ln:
Ned Deilye1c9794952013-01-29 00:07:46 -080098 _cache_getFullVersion = ln.split()[-1][1:-1]
99 return _cache_getFullVersion
100 raise RuntimeError("Cannot find full version??")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000101
Ned Deily62a86602014-12-09 23:45:13 -0800102FW_PREFIX = ["Library", "Frameworks", "Python.framework"]
103FW_VERSION_PREFIX = "--undefined--" # initialized in parseOptions
104
Ronald Oussorenaa560962006-11-07 15:53:38 +0000105# The directory we'll use to create the build (will be erased and recreated)
Ronald Oussoren158ad592006-11-07 16:00:34 +0000106WORKDIR = "/tmp/_py"
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000107
Ronald Oussorenaa560962006-11-07 15:53:38 +0000108# The directory we'll use to store third-party sources. Set this to something
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000109# else if you don't want to re-fetch required libraries every time.
Ronald Oussoren158ad592006-11-07 16:00:34 +0000110DEPSRC = os.path.join(WORKDIR, 'third-party')
111DEPSRC = os.path.expanduser('~/Universal/other-sources')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000112
113# Location of the preferred SDK
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000114
115### There are some issues with the SDK selection below here,
116### The resulting binary doesn't work on all platforms that
117### it should. Always default to the 10.4u SDK until that
Ezio Melotti6d0f0f22013-08-26 01:31:30 +0300118### issue is resolved.
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000119###
120##if int(os.uname()[2].split('.')[0]) == 8:
121## # Explicitly use the 10.4u (universal) SDK when
122## # building on 10.4, the system headers are not
123## # useable for a universal build
124## SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk"
125##else:
126## SDKPATH = "/"
127
128SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk"
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000129
Ronald Oussoren508282e2009-03-30 19:34:51 +0000130universal_opts_map = { '32-bit': ('i386', 'ppc',),
131 '64-bit': ('x86_64', 'ppc64',),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000132 'intel': ('i386', 'x86_64'),
133 '3-way': ('ppc', 'i386', 'x86_64'),
134 'all': ('i386', 'ppc', 'x86_64', 'ppc64',) }
135default_target_map = {
136 '64-bit': '10.5',
137 '3-way': '10.5',
138 'intel': '10.5',
139 'all': '10.5',
140}
Ronald Oussoren508282e2009-03-30 19:34:51 +0000141
142UNIVERSALOPTS = tuple(universal_opts_map.keys())
143
144UNIVERSALARCHS = '32-bit'
145
146ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Ronald Oussoren9b8b6192006-06-27 12:53:52 +0000147
Ezio Melotti24b07bc2011-03-15 18:55:01 +0200148# Source directory (assume we're in Mac/BuildScript)
Ronald Oussoren158ad592006-11-07 16:00:34 +0000149SRCDIR = os.path.dirname(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000150 os.path.dirname(
151 os.path.dirname(
152 os.path.abspath(__file__
153 ))))
154
Ronald Oussoren508282e2009-03-30 19:34:51 +0000155# $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level
156DEPTARGET = '10.3'
157
Ned Deily1f70b872014-06-25 13:33:57 -0700158def getDeptargetTuple():
159 return tuple([int(n) for n in DEPTARGET.split('.')[0:2]])
160
161def getTargetCompilers():
162 target_cc_map = {
Ned Deilye1c9794952013-01-29 00:07:46 -0800163 '10.3': ('gcc-4.0', 'g++-4.0'),
164 '10.4': ('gcc-4.0', 'g++-4.0'),
165 '10.5': ('gcc-4.2', 'g++-4.2'),
166 '10.6': ('gcc-4.2', 'g++-4.2'),
Ned Deily1f70b872014-06-25 13:33:57 -0700167 }
168 return target_cc_map.get(DEPTARGET, ('clang', 'clang++') )
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000169
Ned Deily1f70b872014-06-25 13:33:57 -0700170CC, CXX = getTargetCompilers()
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000171
Ned Deily62a86602014-12-09 23:45:13 -0800172PYTHON_3 = getVersionMajorMinor() >= (3, 0)
Ned Deily53c460d2011-01-30 01:43:40 +0000173
Ronald Oussoren158ad592006-11-07 16:00:34 +0000174USAGE = textwrap.dedent("""\
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000175 Usage: build_python [options]
176
177 Options:
178 -? or -h: Show this message
179 -b DIR
180 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
181 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
182 --sdk-path=DIR: Location of the SDK (default: %(SDKPATH)r)
183 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ronald Oussoren508282e2009-03-30 19:34:51 +0000184 --dep-target=10.n OS X deployment target (default: %(DEPTARGET)r)
185 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000186""")% globals()
187
Ned Deilye1c9794952013-01-29 00:07:46 -0800188# Dict of object file names with shared library names to check after building.
189# This is to ensure that we ended up dynamically linking with the shared
190# library paths and versions we expected. For example:
191# EXPECTED_SHARED_LIBS['_tkinter.so'] = [
192# '/Library/Frameworks/Tcl.framework/Versions/8.5/Tcl',
193# '/Library/Frameworks/Tk.framework/Versions/8.5/Tk']
194EXPECTED_SHARED_LIBS = {}
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000195
Ned Deily62a86602014-12-09 23:45:13 -0800196# List of names of third party software built with this installer.
197# The names will be inserted into the rtf version of the License.
198THIRD_PARTY_LIBS = []
199
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000200# Instructions for building libraries that are necessary for building a
201# batteries included python.
Ronald Oussoren508282e2009-03-30 19:34:51 +0000202# [The recipes are defined here for convenience but instantiated later after
203# command line options have been processed.]
204def library_recipes():
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000205 result = []
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000206
Ned Deily1f70b872014-06-25 13:33:57 -0700207 LT_10_5 = bool(getDeptargetTuple() < (10, 5))
Ned Deilye1c9794952013-01-29 00:07:46 -0800208
Ned Deily942f3de2016-02-25 01:01:02 +1100209 if not (10, 5) < getDeptargetTuple() < (10, 10):
Ned Deily62a86602014-12-09 23:45:13 -0800210 # The OpenSSL libs shipped with OS X 10.5 and earlier are
211 # hopelessly out-of-date and do not include Apple's tie-in to
212 # the root certificates in the user and system keychains via TEA
213 # that was introduced in OS X 10.6. Note that this applies to
214 # programs built and linked with a 10.5 SDK even when run on
215 # newer versions of OS X.
216 #
217 # Dealing with CAs is messy. For now, just supply a
218 # local libssl and libcrypto for the older installer variants
219 # (e.g. the python.org 10.5+ 32-bit-only installer) that use the
220 # same default ssl certfile location as the system libs do:
221 # /System/Library/OpenSSL/cert.pem
222 # Then at least TLS connections can be negotiated with sites that
223 # use sha-256 certs like python.org, assuming the proper CA certs
224 # have been supplied. The default CA cert management issues for
225 # 10.5 and earlier builds are the same as before, other than it is
226 # now more obvious with cert checking enabled by default in the
227 # standard library.
228 #
Ned Deily942f3de2016-02-25 01:01:02 +1100229 # For builds with 10.6 through 10.9 SDKs,
230 # continue to use the deprecated but
Ned Deily62a86602014-12-09 23:45:13 -0800231 # less out-of-date Apple 0.9.8 libs for now. While they are less
232 # secure than using an up-to-date 1.0.1 version, doing so
233 # avoids the big problems of forcing users to have to manage
234 # default CAs themselves, thanks to the Apple libs using private TEA
235 # APIs for cert validation from keychains if validation using the
236 # standard OpenSSL locations (/System/Library/OpenSSL, normally empty)
237 # fails.
Ned Deily942f3de2016-02-25 01:01:02 +1100238 #
239 # Since Apple removed the header files for the deprecated system
240 # OpenSSL as of the Xcode 7 release (for OS X 10.10+), we do not
241 # have much choice but to build our own copy here, too.
Ned Deily62a86602014-12-09 23:45:13 -0800242
243 result.extend([
244 dict(
Ned Deily8d41de62017-07-24 04:02:29 -0400245 name="OpenSSL 1.0.2k",
246 url="https://www.openssl.org/source/openssl-1.0.2k.tar.gz",
247 checksum='f965fc0bf01bf882b31314b61391ae65',
Ned Deily62a86602014-12-09 23:45:13 -0800248 patches=[
249 "openssl_sdk_makedepend.patch",
250 ],
251 buildrecipe=build_universal_openssl,
252 configure=None,
253 install=None,
254 ),
255 ])
256
Ned Deilyebd63dc2014-04-09 16:12:11 -0700257# Disable for now
Ned Deily30101822014-11-14 18:53:59 -0800258 if False: # if getDeptargetTuple() > (10, 5):
Ned Deily0203a802013-10-25 00:40:07 -0700259 result.extend([
260 dict(
261 name="Tcl 8.5.15",
262 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_5/tcl8.5.15-src.tar.gz",
263 checksum='f3df162f92c69b254079c4d0af7a690f',
264 buildDir="unix",
265 configure_pre=[
266 '--enable-shared',
267 '--enable-threads',
268 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
269 ],
270 useLDFlags=False,
271 install='make TCL_LIBRARY=%(TCL_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
272 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
273 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.5'%(getVersion())),
274 },
275 ),
276 dict(
277 name="Tk 8.5.15",
278 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_5/tk8.5.15-src.tar.gz",
279 checksum='55b8e33f903210a4e1c8bce0f820657f',
Ned Deilya6cbff02013-10-27 19:47:23 -0700280 patches=[
281 "issue19373_tk_8_5_15_source.patch",
282 ],
Ned Deily0203a802013-10-25 00:40:07 -0700283 buildDir="unix",
284 configure_pre=[
285 '--enable-aqua',
286 '--enable-shared',
287 '--enable-threads',
288 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
289 ],
290 useLDFlags=False,
291 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'%{
292 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
293 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.5'%(getVersion())),
294 "TK_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tk8.5'%(getVersion())),
295 },
296 ),
297 ])
298
Ned Deily30101822014-11-14 18:53:59 -0800299 if PYTHON_3:
Ned Deilye1c9794952013-01-29 00:07:46 -0800300 result.extend([
301 dict(
Ned Deilyebd63dc2014-04-09 16:12:11 -0700302 name="XZ 5.0.5",
303 url="http://tukaani.org/xz/xz-5.0.5.tar.gz",
304 checksum='19d924e066b6fff0bc9d1981b4e53196',
Ned Deilye1c9794952013-01-29 00:07:46 -0800305 configure_pre=[
306 '--disable-dependency-tracking',
307 ]
308 ),
309 ])
310
311 result.extend([
312 dict(
313 name="NCurses 5.9",
314 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
315 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
316 configure_pre=[
317 "--enable-widec",
318 "--without-cxx",
319 "--without-cxx-binding",
320 "--without-ada",
321 "--without-curses-h",
322 "--enable-shared",
323 "--with-shared",
324 "--without-debug",
325 "--without-normal",
326 "--without-tests",
327 "--without-manpages",
328 "--datadir=/usr/share",
329 "--sysconfdir=/etc",
330 "--sharedstatedir=/usr/com",
331 "--with-terminfo-dirs=/usr/share/terminfo",
332 "--with-default-terminfo-dir=/usr/share/terminfo",
333 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
334 ],
335 patchscripts=[
336 ("ftp://invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
337 "f54bf02a349f96a7c4f0d00922f3a0d4"),
338 ],
339 useLDFlags=False,
340 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
341 shellQuote(os.path.join(WORKDIR, 'libraries')),
342 shellQuote(os.path.join(WORKDIR, 'libraries')),
343 getVersion(),
344 ),
345 ),
346 dict(
Ned Deilyebd63dc2014-04-09 16:12:11 -0700347 name="SQLite 3.8.3.1",
348 url="http://www.sqlite.org/2014/sqlite-autoconf-3080301.tar.gz",
349 checksum='509ff98d8dc9729b618b7e96612079c6',
Ned Deilye1c9794952013-01-29 00:07:46 -0800350 extra_cflags=('-Os '
351 '-DSQLITE_ENABLE_FTS4 '
352 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
353 '-DSQLITE_ENABLE_RTREE '
354 '-DSQLITE_TCL=0 '
355 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
356 configure_pre=[
357 '--enable-threadsafe',
358 '--enable-shared=no',
359 '--enable-static=yes',
360 '--disable-readline',
361 '--disable-dependency-tracking',
362 ]
363 ),
364 ])
365
Ned Deily1f70b872014-06-25 13:33:57 -0700366 if getDeptargetTuple() < (10, 5):
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000367 result.extend([
368 dict(
Ned Deily53c460d2011-01-30 01:43:40 +0000369 name="Bzip2 1.0.6",
370 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
371 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000372 configure=None,
Ned Deilye1c9794952013-01-29 00:07:46 -0800373 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
374 CC, CXX,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000375 shellQuote(os.path.join(WORKDIR, 'libraries')),
376 ' -arch '.join(ARCHLIST),
377 SDKPATH,
Ronald Oussoren508282e2009-03-30 19:34:51 +0000378 ),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000379 ),
380 dict(
381 name="ZLib 1.2.3",
382 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
383 checksum='debc62758716a169df9f62e6ab2bc634',
384 configure=None,
Ned Deilye1c9794952013-01-29 00:07:46 -0800385 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
386 CC, CXX,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000387 shellQuote(os.path.join(WORKDIR, 'libraries')),
388 ' -arch '.join(ARCHLIST),
389 SDKPATH,
390 ),
391 ),
392 dict(
393 # Note that GNU readline is GPL'd software
Ned Deily53c460d2011-01-30 01:43:40 +0000394 name="GNU Readline 6.1.2",
395 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
396 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000397 patchlevel='0',
398 patches=[
399 # The readline maintainers don't do actual micro releases, but
400 # just ship a set of patches.
Ned Deilye1c9794952013-01-29 00:07:46 -0800401 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
402 'c642f2e84d820884b0bf9fd176bc6c3f'),
403 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
404 '1a76781a1ea734e831588285db7ec9b1'),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000405 ]
406 ),
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000407 ])
408
Ned Deily53c460d2011-01-30 01:43:40 +0000409 if not PYTHON_3:
410 result.extend([
411 dict(
412 name="Sleepycat DB 4.7.25",
413 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
414 checksum='ec2b87e833779681a0c3a814aa71359e',
415 buildDir="build_unix",
416 configure="../dist/configure",
417 configure_pre=[
418 '--includedir=/usr/local/include/db4',
419 ]
420 ),
421 ])
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000422
423 return result
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000424
425
426# Instructions for building packages inside the .mpkg.
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000427def pkg_recipes():
Ned Deily53c460d2011-01-30 01:43:40 +0000428 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000429 result = [
430 dict(
431 name="PythonFramework",
432 long_name="Python Framework",
433 source="/Library/Frameworks/Python.framework",
434 readme="""\
435 This package installs Python.framework, that is the python
436 interpreter and the standard library. This also includes Python
437 wrappers for lots of Mac OS X API's.
438 """,
439 postflight="scripts/postflight.framework",
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000440 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000441 ),
442 dict(
443 name="PythonApplications",
444 long_name="GUI Applications",
445 source="/Applications/Python %(VER)s",
446 readme="""\
447 This package installs IDLE (an interactive Python IDE),
448 Python Launcher and Build Applet (create application bundles
449 from python scripts).
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000450
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000451 It also installs a number of examples and demos.
452 """,
453 required=False,
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000454 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000455 ),
456 dict(
457 name="PythonUnixTools",
458 long_name="UNIX command-line tools",
459 source="/usr/local/bin",
460 readme="""\
461 This package installs the unix tools in /usr/local/bin for
462 compatibility with older releases of Python. This package
463 is not necessary to use Python.
464 """,
465 required=False,
Ronald Oussoren2f4f63a2010-07-23 11:11:26 +0000466 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000467 ),
468 dict(
469 name="PythonDocumentation",
470 long_name="Python Documentation",
471 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
472 source="/pydocs",
473 readme="""\
474 This package installs the python documentation at a location
Ned Deilye1c9794952013-01-29 00:07:46 -0800475 that is useable for pydoc and IDLE.
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000476 """,
477 postflight="scripts/postflight.documentation",
478 required=False,
Ronald Oussoren1a13cff2009-12-24 13:30:42 +0000479 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000480 ),
481 dict(
482 name="PythonProfileChanges",
483 long_name="Shell profile updater",
484 readme="""\
485 This packages updates your shell profile to make sure that
486 the Python tools are found by your shell in preference of
487 the system provided Python tools.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000488
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000489 If you don't install this package you'll have to add
490 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
491 to your PATH by hand.
492 """,
493 postflight="scripts/postflight.patch-profile",
494 topdir="/Library/Frameworks/Python.framework",
495 source="/empty-dir",
496 required=False,
Ned Deily30101822014-11-14 18:53:59 -0800497 selected='selected',
498 ),
499 dict(
500 name="PythonInstallPip",
501 long_name="Install or upgrade pip",
502 readme="""\
503 This package installs (or upgrades from an earlier version)
504 pip, a tool for installing and managing Python packages.
505 """,
506 postflight="scripts/postflight.ensurepip",
507 topdir="/Library/Frameworks/Python.framework",
508 source="/empty-dir",
509 required=False,
510 selected='selected',
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000511 ),
512 ]
513
Ned Deily1f70b872014-06-25 13:33:57 -0700514 if getDeptargetTuple() < (10, 4) and not PYTHON_3:
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000515 result.append(
516 dict(
517 name="PythonSystemFixes",
518 long_name="Fix system Python",
519 readme="""\
520 This package updates the system python installation on
521 Mac OS X 10.3 to ensure that you can build new python extensions
522 using that copy of python after installing this version.
523 """,
524 postflight="../Tools/fixapplepython23.py",
525 topdir="/Library/Frameworks/Python.framework",
526 source="/empty-dir",
527 required=False,
Ned Deily53c460d2011-01-30 01:43:40 +0000528 selected=unselected_for_python3,
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000529 )
530 )
Ned Deilyebd63dc2014-04-09 16:12:11 -0700531
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000532 return result
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000533
534def fatal(msg):
535 """
536 A fatal error, bail out.
537 """
538 sys.stderr.write('FATAL: ')
539 sys.stderr.write(msg)
540 sys.stderr.write('\n')
541 sys.exit(1)
542
543def fileContents(fn):
544 """
545 Return the contents of the named file
546 """
Ned Deilye1c9794952013-01-29 00:07:46 -0800547 return open(fn, 'r').read()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000548
549def runCommand(commandline):
550 """
Ezio Melottic2077b02011-03-16 12:34:31 +0200551 Run a command and raise RuntimeError if it fails. Output is suppressed
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000552 unless the command fails.
553 """
554 fd = os.popen(commandline, 'r')
555 data = fd.read()
556 xit = fd.close()
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000557 if xit is not None:
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000558 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800559 raise RuntimeError("command failed: %s"%(commandline,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000560
561 if VERBOSE:
562 sys.stdout.write(data); sys.stdout.flush()
563
564def captureCommand(commandline):
565 fd = os.popen(commandline, 'r')
566 data = fd.read()
567 xit = fd.close()
Benjamin Peterson5b63acd2008-03-29 15:24:25 +0000568 if xit is not None:
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000569 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800570 raise RuntimeError("command failed: %s"%(commandline,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000571
572 return data
573
Ronald Oussoren287128a2010-04-18 14:01:05 +0000574def getTclTkVersion(configfile, versionline):
575 """
576 search Tcl or Tk configuration file for version line
577 """
578 try:
579 f = open(configfile, "r")
Ned Deily123a58b2017-07-24 04:29:32 -0400580 except OSError:
Ronald Oussoren287128a2010-04-18 14:01:05 +0000581 fatal("Framework configuration file not found: %s" % configfile)
582
583 for l in f:
584 if l.startswith(versionline):
585 f.close()
586 return l
587
588 fatal("Version variable %s not found in framework configuration file: %s"
589 % (versionline, configfile))
590
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000591def checkEnvironment():
592 """
593 Check that we're running on a supported system.
594 """
595
Ned Deily53c460d2011-01-30 01:43:40 +0000596 if sys.version_info[0:2] < (2, 4):
597 fatal("This script must be run with Python 2.4 or later")
598
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000599 if platform.system() != 'Darwin':
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000600 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000601
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000602 if int(platform.release().split('.')[0]) < 8:
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000603 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000604
605 if not os.path.exists(SDKPATH):
606 fatal("Please install the latest version of Xcode and the %s SDK"%(
607 os.path.basename(SDKPATH[:-4])))
608
Ronald Oussoren287128a2010-04-18 14:01:05 +0000609 # Because we only support dynamic load of only one major/minor version of
610 # Tcl/Tk, ensure:
611 # 1. there are no user-installed frameworks of Tcl/Tk with version
Ned Deilye1c9794952013-01-29 00:07:46 -0800612 # higher than the Apple-supplied system version in
613 # SDKROOT/System/Library/Frameworks
614 # 2. there is a user-installed framework (usually ActiveTcl) in (or linked
615 # in) SDKROOT/Library/Frameworks with the same version as the system
616 # version. This allows users to choose to install a newer patch level.
Ronald Oussoren287128a2010-04-18 14:01:05 +0000617
Ned Deilye1c9794952013-01-29 00:07:46 -0800618 frameworks = {}
Ronald Oussoren287128a2010-04-18 14:01:05 +0000619 for framework in ['Tcl', 'Tk']:
Ned Deilye1c9794952013-01-29 00:07:46 -0800620 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
Ned Deily53c460d2011-01-30 01:43:40 +0000621 sysfw = os.path.join(SDKPATH, 'System', fwpth)
Ned Deilye1c9794952013-01-29 00:07:46 -0800622 libfw = os.path.join(SDKPATH, fwpth)
Ronald Oussoren287128a2010-04-18 14:01:05 +0000623 usrfw = os.path.join(os.getenv('HOME'), fwpth)
Ned Deilye1c9794952013-01-29 00:07:46 -0800624 frameworks[framework] = os.readlink(sysfw)
625 if not os.path.exists(libfw):
626 fatal("Please install a link to a current %s %s as %s so "
627 "the user can override the system framework."
628 % (framework, frameworks[framework], libfw))
Ned Deily53c460d2011-01-30 01:43:40 +0000629 if os.readlink(libfw) != os.readlink(sysfw):
Ronald Oussoren287128a2010-04-18 14:01:05 +0000630 fatal("Version of %s must match %s" % (libfw, sysfw) )
631 if os.path.exists(usrfw):
632 fatal("Please rename %s to avoid possible dynamic load issues."
633 % usrfw)
634
Ned Deilye1c9794952013-01-29 00:07:46 -0800635 if frameworks['Tcl'] != frameworks['Tk']:
636 fatal("The Tcl and Tk frameworks are not the same version.")
637
638 # add files to check after build
639 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
640 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
641 % frameworks['Tcl'],
642 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
643 % frameworks['Tk'],
644 ]
645
Ronald Oussoren287128a2010-04-18 14:01:05 +0000646 # Remove inherited environment variables which might influence build
647 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
648 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
649 for ev in list(os.environ):
650 for prefix in environ_var_prefixes:
651 if ev.startswith(prefix) :
Ned Deilye1c9794952013-01-29 00:07:46 -0800652 print("INFO: deleting environment variable %s=%s" % (
653 ev, os.environ[ev]))
Ronald Oussoren287128a2010-04-18 14:01:05 +0000654 del os.environ[ev]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000655
Ned Deilye1c9794952013-01-29 00:07:46 -0800656 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
657 if 'SDK_TOOLS_BIN' in os.environ:
658 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
659 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
660 # add its fixed location here if it exists
661 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
662 if os.path.isdir(OLD_DEVELOPER_TOOLS):
663 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
664 os.environ['PATH'] = base_path
665 print("Setting default PATH: %s"%(os.environ['PATH']))
Ned Deily123a58b2017-07-24 04:29:32 -0400666 # Ensure we have access to sphinx-build.
667 # You may have to create a link in /usr/bin for it.
Ned Deily5ceffa12014-09-05 15:51:54 -0700668 runCommand('sphinx-build --version')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000669
Ronald Oussoren158ad592006-11-07 16:00:34 +0000670def parseOptions(args=None):
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000671 """
672 Parse arguments and update global settings.
673 """
Ronald Oussoren508282e2009-03-30 19:34:51 +0000674 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
Ned Deilye1c9794952013-01-29 00:07:46 -0800675 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily62a86602014-12-09 23:45:13 -0800676 global FW_VERSION_PREFIX
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000677
678 if args is None:
679 args = sys.argv[1:]
680
681 try:
682 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren508282e2009-03-30 19:34:51 +0000683 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
684 'dep-target=', 'universal-archs=', 'help' ])
Ned Deilye1c9794952013-01-29 00:07:46 -0800685 except getopt.GetoptError:
686 print(sys.exc_info()[1])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000687 sys.exit(1)
688
689 if args:
Ned Deilye1c9794952013-01-29 00:07:46 -0800690 print("Additional arguments")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000691 sys.exit(1)
692
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000693 deptarget = None
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000694 for k, v in options:
Ronald Oussoren508282e2009-03-30 19:34:51 +0000695 if k in ('-h', '-?', '--help'):
Ned Deilye1c9794952013-01-29 00:07:46 -0800696 print(USAGE)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000697 sys.exit(0)
698
699 elif k in ('-d', '--build-dir'):
700 WORKDIR=v
701
702 elif k in ('--third-party',):
703 DEPSRC=v
704
705 elif k in ('--sdk-path',):
706 SDKPATH=v
707
708 elif k in ('--src-dir',):
709 SRCDIR=v
710
Ronald Oussoren508282e2009-03-30 19:34:51 +0000711 elif k in ('--dep-target', ):
712 DEPTARGET=v
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000713 deptarget=v
Ronald Oussoren508282e2009-03-30 19:34:51 +0000714
715 elif k in ('--universal-archs', ):
716 if v in UNIVERSALOPTS:
717 UNIVERSALARCHS = v
718 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000719 if deptarget is None:
720 # Select alternate default deployment
721 # target
722 DEPTARGET = default_target_map.get(v, '10.3')
Ronald Oussoren508282e2009-03-30 19:34:51 +0000723 else:
Ned Deilye1c9794952013-01-29 00:07:46 -0800724 raise NotImplementedError(v)
Ronald Oussoren508282e2009-03-30 19:34:51 +0000725
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000726 else:
Ned Deilye1c9794952013-01-29 00:07:46 -0800727 raise NotImplementedError(k)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000728
729 SRCDIR=os.path.abspath(SRCDIR)
730 WORKDIR=os.path.abspath(WORKDIR)
731 SDKPATH=os.path.abspath(SDKPATH)
732 DEPSRC=os.path.abspath(DEPSRC)
733
Ned Deily1f70b872014-06-25 13:33:57 -0700734 CC, CXX = getTargetCompilers()
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000735
Ned Deily62a86602014-12-09 23:45:13 -0800736 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
737
738 print("-- Settings:")
739 print(" * Source directory: %s" % SRCDIR)
740 print(" * Build directory: %s" % WORKDIR)
741 print(" * SDK location: %s" % SDKPATH)
742 print(" * Third-party source: %s" % DEPSRC)
743 print(" * Deployment target: %s" % DEPTARGET)
744 print(" * Universal archs: %s" % str(ARCHLIST))
745 print(" * C compiler: %s" % CC)
746 print(" * C++ compiler: %s" % CXX)
Ned Deilye1c9794952013-01-29 00:07:46 -0800747 print("")
Ned Deily62a86602014-12-09 23:45:13 -0800748 print(" -- Building a Python %s framework at patch level %s"
749 % (getVersion(), getFullVersion()))
750 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000751
752def extractArchive(builddir, archiveName):
753 """
754 Extract a source archive into 'builddir'. Returns the path of the
755 extracted archive.
756
757 XXX: This function assumes that archives contain a toplevel directory
758 that is has the same name as the basename of the archive. This is
Ned Deily0203a802013-10-25 00:40:07 -0700759 safe enough for almost anything we use. Unfortunately, it does not
760 work for current Tcl and Tk source releases where the basename of
761 the archive ends with "-src" but the uncompressed directory does not.
762 For now, just special case Tcl and Tk tar.gz downloads.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000763 """
764 curdir = os.getcwd()
765 try:
766 os.chdir(builddir)
767 if archiveName.endswith('.tar.gz'):
768 retval = os.path.basename(archiveName[:-7])
Ned Deily0203a802013-10-25 00:40:07 -0700769 if ((retval.startswith('tcl') or retval.startswith('tk'))
770 and retval.endswith('-src')):
771 retval = retval[:-4]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000772 if os.path.exists(retval):
773 shutil.rmtree(retval)
774 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
775
776 elif archiveName.endswith('.tar.bz2'):
777 retval = os.path.basename(archiveName[:-8])
778 if os.path.exists(retval):
779 shutil.rmtree(retval)
780 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
781
782 elif archiveName.endswith('.tar'):
783 retval = os.path.basename(archiveName[:-4])
784 if os.path.exists(retval):
785 shutil.rmtree(retval)
786 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
787
788 elif archiveName.endswith('.zip'):
789 retval = os.path.basename(archiveName[:-4])
790 if os.path.exists(retval):
791 shutil.rmtree(retval)
792 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
793
794 data = fp.read()
795 xit = fp.close()
796 if xit is not None:
797 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800798 raise RuntimeError("Cannot extract %s"%(archiveName,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000799
800 return os.path.join(builddir, retval)
801
802 finally:
803 os.chdir(curdir)
804
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000805def downloadURL(url, fname):
806 """
807 Download the contents of the url into the file.
808 """
Ned Deilye1c9794952013-01-29 00:07:46 -0800809 fpIn = urllib_request.urlopen(url)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000810 fpOut = open(fname, 'wb')
811 block = fpIn.read(10240)
812 try:
813 while block:
814 fpOut.write(block)
815 block = fpIn.read(10240)
816 fpIn.close()
817 fpOut.close()
818 except:
819 try:
820 os.unlink(fname)
Ned Deily123a58b2017-07-24 04:29:32 -0400821 except OSError:
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000822 pass
823
Ned Deilye1c9794952013-01-29 00:07:46 -0800824def verifyThirdPartyFile(url, checksum, fname):
825 """
826 Download file from url to filename fname if it does not already exist.
827 Abort if file contents does not match supplied md5 checksum.
828 """
829 name = os.path.basename(fname)
830 if os.path.exists(fname):
831 print("Using local copy of %s"%(name,))
832 else:
833 print("Did not find local copy of %s"%(name,))
834 print("Downloading %s"%(name,))
835 downloadURL(url, fname)
836 print("Archive for %s stored as %s"%(name, fname))
837 if os.system(
838 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
839 % (shellQuote(fname), checksum) ):
840 fatal('MD5 checksum mismatch for file %s' % fname)
841
Ned Deily62a86602014-12-09 23:45:13 -0800842def build_universal_openssl(basedir, archList):
843 """
844 Special case build recipe for universal build of openssl.
845
846 The upstream OpenSSL build system does not directly support
847 OS X universal builds. We need to build each architecture
848 separately then lipo them together into fat libraries.
849 """
850
Ned Deily0be4b1e2014-12-11 15:55:42 -0800851 # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4).
852 # If we are building on a 10.4.x or earlier system,
853 # unilaterally disable assembly code building to avoid the problem.
854 no_asm = int(platform.release().split(".")[0]) < 9
855
Ned Deily62a86602014-12-09 23:45:13 -0800856 def build_openssl_arch(archbase, arch):
857 "Build one architecture of openssl"
858 arch_opts = {
859 "i386": ["darwin-i386-cc"],
860 "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
861 "ppc": ["darwin-ppc-cc"],
862 "ppc64": ["darwin64-ppc-cc"],
863 }
864 configure_opts = [
865 "no-krb5",
866 "no-idea",
867 "no-mdc2",
868 "no-rc5",
869 "no-zlib",
870 "enable-tlsext",
871 "no-ssl2",
872 "no-ssl3",
873 "no-ssl3-method",
874 # "enable-unit-test",
875 "shared",
876 "--install_prefix=%s"%shellQuote(archbase),
877 "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX),
878 "--openssldir=/System/Library/OpenSSL",
879 ]
Ned Deily0be4b1e2014-12-11 15:55:42 -0800880 if no_asm:
881 configure_opts.append("no-asm")
Ned Deily62a86602014-12-09 23:45:13 -0800882 runCommand(" ".join(["perl", "Configure"]
883 + arch_opts[arch] + configure_opts))
884 runCommand("make depend OSX_SDK=%s" % SDKPATH)
885 runCommand("make all OSX_SDK=%s" % SDKPATH)
886 runCommand("make install_sw OSX_SDK=%s" % SDKPATH)
887 # runCommand("make test")
888 return
889
890 srcdir = os.getcwd()
891 universalbase = os.path.join(srcdir, "..",
892 os.path.basename(srcdir) + "-universal")
893 os.mkdir(universalbase)
894 archbasefws = []
895 for arch in archList:
896 # fresh copy of the source tree
897 archsrc = os.path.join(universalbase, arch, "src")
898 shutil.copytree(srcdir, archsrc, symlinks=True)
899 # install base for this arch
900 archbase = os.path.join(universalbase, arch, "root")
901 os.mkdir(archbase)
902 # Python framework base within install_prefix:
903 # the build will install into this framework..
904 # This is to ensure that the resulting shared libs have
905 # the desired real install paths built into them.
906 archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX)
907
908 # build one architecture
909 os.chdir(archsrc)
910 build_openssl_arch(archbase, arch)
911 os.chdir(srcdir)
912 archbasefws.append(archbasefw)
913
914 # copy arch-independent files from last build into the basedir framework
915 basefw = os.path.join(basedir, *FW_VERSION_PREFIX)
916 shutil.copytree(
917 os.path.join(archbasefw, "include", "openssl"),
918 os.path.join(basefw, "include", "openssl")
919 )
920
921 shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"),
922 "SHLIB_VERSION_NUMBER")
923 # e.g. -> "1.0.0"
924 libcrypto = "libcrypto.dylib"
925 libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".")
926 # e.g. -> "libcrypto.1.0.0.dylib"
927 libssl = "libssl.dylib"
928 libssl_versioned = libssl.replace(".", "."+shlib_version_number+".")
929 # e.g. -> "libssl.1.0.0.dylib"
930
931 try:
932 os.mkdir(os.path.join(basefw, "lib"))
933 except OSError:
934 pass
935
936 # merge the individual arch-dependent shared libs into a fat shared lib
937 archbasefws.insert(0, basefw)
938 for (lib_unversioned, lib_versioned) in [
939 (libcrypto, libcrypto_versioned),
940 (libssl, libssl_versioned)
941 ]:
942 runCommand("lipo -create -output " +
943 " ".join(shellQuote(
944 os.path.join(fw, "lib", lib_versioned))
945 for fw in archbasefws))
946 # and create an unversioned symlink of it
947 os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned))
948
949 # Create links in the temp include and lib dirs that will be injected
950 # into the Python build so that setup.py can find them while building
951 # and the versioned links so that the setup.py post-build import test
952 # does not fail.
953 relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX)
954 for fn in [
955 ["include", "openssl"],
956 ["lib", libcrypto],
957 ["lib", libssl],
958 ["lib", libcrypto_versioned],
959 ["lib", libssl_versioned],
960 ]:
961 os.symlink(
962 os.path.join(relative_path, *fn),
963 os.path.join(basedir, "usr", "local", *fn)
964 )
965
966 return
967
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000968def buildRecipe(recipe, basedir, archList):
969 """
970 Build software using a recipe. This function does the
971 'configure;make;make install' dance for C software, with a possibility
972 to customize this process, basically a poor-mans DarwinPorts.
973 """
974 curdir = os.getcwd()
975
976 name = recipe['name']
Ned Deily62a86602014-12-09 23:45:13 -0800977 THIRD_PARTY_LIBS.append(name)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000978 url = recipe['url']
979 configure = recipe.get('configure', './configure')
Ned Deily62a86602014-12-09 23:45:13 -0800980 buildrecipe = recipe.get('buildrecipe', None)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000981 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
982 shellQuote(basedir)))
983
984 archiveName = os.path.split(url)[-1]
985 sourceArchive = os.path.join(DEPSRC, archiveName)
986
987 if not os.path.exists(DEPSRC):
988 os.mkdir(DEPSRC)
989
Ned Deilye1c9794952013-01-29 00:07:46 -0800990 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
991 print("Extracting archive for %s"%(name,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000992 buildDir=os.path.join(WORKDIR, '_bld')
993 if not os.path.exists(buildDir):
994 os.mkdir(buildDir)
995
996 workDir = extractArchive(buildDir, sourceArchive)
997 os.chdir(workDir)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000998
Ned Deilye1c9794952013-01-29 00:07:46 -0800999 for patch in recipe.get('patches', ()):
1000 if isinstance(patch, tuple):
1001 url, checksum = patch
1002 fn = os.path.join(DEPSRC, os.path.basename(url))
1003 verifyThirdPartyFile(url, checksum, fn)
1004 else:
1005 # patch is a file in the source directory
1006 fn = os.path.join(curdir, patch)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001007 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
1008 shellQuote(fn),))
1009
Ned Deilye1c9794952013-01-29 00:07:46 -08001010 for patchscript in recipe.get('patchscripts', ()):
1011 if isinstance(patchscript, tuple):
1012 url, checksum = patchscript
1013 fn = os.path.join(DEPSRC, os.path.basename(url))
1014 verifyThirdPartyFile(url, checksum, fn)
1015 else:
1016 # patch is a file in the source directory
1017 fn = os.path.join(curdir, patchscript)
1018 if fn.endswith('.bz2'):
1019 runCommand('bunzip2 -fk %s' % shellQuote(fn))
1020 fn = fn[:-4]
1021 runCommand('sh %s' % shellQuote(fn))
1022 os.unlink(fn)
1023
Ned Deilya6cbff02013-10-27 19:47:23 -07001024 if 'buildDir' in recipe:
1025 os.chdir(recipe['buildDir'])
1026
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001027 if configure is not None:
1028 configure_args = [
1029 "--prefix=/usr/local",
1030 "--enable-static",
1031 "--disable-shared",
1032 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
1033 ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001034
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001035 if 'configure_pre' in recipe:
1036 args = list(recipe['configure_pre'])
1037 if '--disable-static' in args:
1038 configure_args.remove('--enable-static')
1039 if '--enable-shared' in args:
1040 configure_args.remove('--disable-shared')
1041 configure_args.extend(args)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001042
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001043 if recipe.get('useLDFlags', 1):
1044 configure_args.extend([
Ned Deilye1c9794952013-01-29 00:07:46 -08001045 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
1046 "-I%s/usr/local/include"%(
1047 recipe.get('extra_cflags', ''),
1048 DEPTARGET,
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001049 ' -arch '.join(archList),
1050 shellQuote(SDKPATH)[1:-1],
1051 shellQuote(basedir)[1:-1],),
Ned Deilyc177b1c2014-04-09 16:13:46 -07001052 "LDFLAGS=-mmacosx-version-min=%s -isysroot %s -L%s/usr/local/lib -arch %s"%(
Ned Deilye1c9794952013-01-29 00:07:46 -08001053 DEPTARGET,
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001054 shellQuote(SDKPATH)[1:-1],
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001055 shellQuote(basedir)[1:-1],
1056 ' -arch '.join(archList)),
1057 ])
1058 else:
1059 configure_args.extend([
Ned Deilye1c9794952013-01-29 00:07:46 -08001060 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
1061 "-I%s/usr/local/include"%(
1062 recipe.get('extra_cflags', ''),
1063 DEPTARGET,
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001064 ' -arch '.join(archList),
1065 shellQuote(SDKPATH)[1:-1],
1066 shellQuote(basedir)[1:-1],),
1067 ])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001068
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001069 if 'configure_post' in recipe:
Ned Deilye1c9794952013-01-29 00:07:46 -08001070 configure_args = configure_args + list(recipe['configure_post'])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001071
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001072 configure_args.insert(0, configure)
1073 configure_args = [ shellQuote(a) for a in configure_args ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001074
Ned Deilye1c9794952013-01-29 00:07:46 -08001075 print("Running configure for %s"%(name,))
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001076 runCommand(' '.join(configure_args) + ' 2>&1')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001077
Ned Deily62a86602014-12-09 23:45:13 -08001078 if buildrecipe is not None:
1079 # call special-case build recipe, e.g. for openssl
1080 buildrecipe(basedir, archList)
1081
1082 if install is not None:
1083 print("Running install for %s"%(name,))
1084 runCommand('{ ' + install + ' ;} 2>&1')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001085
Ned Deilye1c9794952013-01-29 00:07:46 -08001086 print("Done %s"%(name,))
1087 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001088
1089 os.chdir(curdir)
1090
1091def buildLibraries():
1092 """
1093 Build our dependencies into $WORKDIR/libraries/usr/local
1094 """
Ned Deilye1c9794952013-01-29 00:07:46 -08001095 print("")
1096 print("Building required libraries")
1097 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001098 universal = os.path.join(WORKDIR, 'libraries')
1099 os.mkdir(universal)
1100 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
1101 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
1102
Ronald Oussoren508282e2009-03-30 19:34:51 +00001103 for recipe in library_recipes():
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001104 buildRecipe(recipe, universal, ARCHLIST)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001105
1106
1107
1108def buildPythonDocs():
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001109 # This stores the documentation as Resources/English.lproj/Documentation
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001110 # inside the framwork. pydoc and IDLE will pick it up there.
Ned Deilye1c9794952013-01-29 00:07:46 -08001111 print("Install python documentation")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001112 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001113 buildDir = os.path.join('../../Doc')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001114 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001115 curDir = os.getcwd()
1116 os.chdir(buildDir)
Ned Deily5ceffa12014-09-05 15:51:54 -07001117 runCommand('make clean')
1118 # Assume sphinx-build is on our PATH, checked in checkEnvironment
1119 runCommand('make html')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001120 os.chdir(curDir)
1121 if not os.path.exists(docdir):
1122 os.mkdir(docdir)
Ronald Oussoren209d4c32009-09-29 19:34:13 +00001123 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001124
1125
1126def buildPython():
Ned Deilye1c9794952013-01-29 00:07:46 -08001127 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001128
1129 buildDir = os.path.join(WORKDIR, '_bld', 'python')
1130 rootDir = os.path.join(WORKDIR, '_root')
1131
1132 if os.path.exists(buildDir):
1133 shutil.rmtree(buildDir)
1134 if os.path.exists(rootDir):
1135 shutil.rmtree(rootDir)
Ned Deily53c460d2011-01-30 01:43:40 +00001136 os.makedirs(buildDir)
1137 os.makedirs(rootDir)
1138 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001139 curdir = os.getcwd()
1140 os.chdir(buildDir)
1141
1142 # Not sure if this is still needed, the original build script
1143 # claims that parts of the install assume python.exe exists.
1144 os.symlink('python', os.path.join(buildDir, 'python.exe'))
1145
1146 # Extract the version from the configure file, needed to calculate
1147 # several paths.
1148 version = getVersion()
1149
Ronald Oussoren008af852009-03-30 20:02:08 +00001150 # Since the extra libs are not in their installed framework location
1151 # during the build, augment the library path so that the interpreter
1152 # will find them during its extension import sanity checks.
1153 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
1154 'libraries', 'usr', 'local', 'lib')
Ned Deilye1c9794952013-01-29 00:07:46 -08001155 print("Running configure...")
Ronald Oussoren508282e2009-03-30 19:34:51 +00001156 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
1157 "--with-universal-archs=%s "
Ned Deily53c460d2011-01-30 01:43:40 +00001158 "%s "
Ned Deilyebd63dc2014-04-09 16:12:11 -07001159 "%s "
Ronald Oussoren508282e2009-03-30 19:34:51 +00001160 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deilyf84b5312013-10-25 00:44:46 -07001161 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001162 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
1163 UNIVERSALARCHS,
Ned Deily53c460d2011-01-30 01:43:40 +00001164 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deily30101822014-11-14 18:53:59 -08001165 (' ', '--without-ensurepip ')[PYTHON_3],
Ronald Oussoren508282e2009-03-30 19:34:51 +00001166 shellQuote(WORKDIR)[1:-1],
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001167 shellQuote(WORKDIR)[1:-1]))
1168
Ned Deily123a58b2017-07-24 04:29:32 -04001169 # Look for environment value BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS
1170 # and, if defined, append its value to the make command. This allows
1171 # us to pass in version control tags, like GITTAG, to a build from a
1172 # tarball rather than from a vcs checkout, thus eliminating the need
1173 # to have a working copy of the vcs program on the build machine.
1174 #
1175 # A typical use might be:
1176 # export BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS=" \
1177 # GITVERSION='echo 123456789a' \
1178 # GITTAG='echo v3.6.0' \
1179 # GITBRANCH='echo 3.6'"
1180
1181 make_extras = os.getenv("BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS")
1182 if make_extras:
1183 make_cmd = "make " + make_extras
1184 else:
1185 make_cmd = "make"
1186 print("Running " + make_cmd)
1187 runCommand(make_cmd)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001188
Ned Deilye1c9794952013-01-29 00:07:46 -08001189 print("Running make install")
Ned Deily53c460d2011-01-30 01:43:40 +00001190 runCommand("make install DESTDIR=%s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001191 shellQuote(rootDir)))
1192
Ned Deilye1c9794952013-01-29 00:07:46 -08001193 print("Running make frameworkinstallextras")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001194 runCommand("make frameworkinstallextras DESTDIR=%s"%(
1195 shellQuote(rootDir)))
1196
Ronald Oussoren008af852009-03-30 20:02:08 +00001197 del os.environ['DYLD_LIBRARY_PATH']
Ned Deilye1c9794952013-01-29 00:07:46 -08001198 print("Copying required shared libraries")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001199 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
1200 runCommand("mv %s/* %s"%(
1201 shellQuote(os.path.join(
1202 WORKDIR, 'libraries', 'Library', 'Frameworks',
1203 'Python.framework', 'Versions', getVersion(),
1204 'lib')),
1205 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
1206 'Python.framework', 'Versions', getVersion(),
1207 'lib'))))
1208
Ned Deily70f213a2013-10-26 03:16:06 -07001209 path_to_lib = os.path.join(rootDir, 'Library', 'Frameworks',
1210 'Python.framework', 'Versions',
1211 version, 'lib', 'python%s'%(version,))
1212
Ned Deilye1c9794952013-01-29 00:07:46 -08001213 print("Fix file modes")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001214 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Ronald Oussoren74d3eef2006-10-10 07:55:06 +00001215 gid = grp.getgrnam('admin').gr_gid
1216
Ned Deilye1c9794952013-01-29 00:07:46 -08001217 shared_lib_error = False
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001218 for dirpath, dirnames, filenames in os.walk(frmDir):
1219 for dn in dirnames:
Ned Deilye1c9794952013-01-29 00:07:46 -08001220 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +00001221 os.chown(os.path.join(dirpath, dn), -1, gid)
Tim Petersef3f32f2006-10-18 05:09:12 +00001222
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001223 for fn in filenames:
1224 if os.path.islink(fn):
1225 continue
1226
1227 # "chmod g+w $fn"
1228 p = os.path.join(dirpath, fn)
1229 st = os.stat(p)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +00001230 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1231 os.chown(p, -1, gid)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001232
Ned Deilye1c9794952013-01-29 00:07:46 -08001233 if fn in EXPECTED_SHARED_LIBS:
1234 # check to see that this file was linked with the
1235 # expected library path and version
1236 data = captureCommand("otool -L %s" % shellQuote(p))
1237 for sl in EXPECTED_SHARED_LIBS[fn]:
1238 if ("\t%s " % sl) not in data:
1239 print("Expected shared lib %s was not linked with %s"
1240 % (sl, p))
1241 shared_lib_error = True
1242
1243 if shared_lib_error:
1244 fatal("Unexpected shared library errors.")
1245
Ned Deily53c460d2011-01-30 01:43:40 +00001246 if PYTHON_3:
1247 LDVERSION=None
1248 VERSION=None
1249 ABIFLAGS=None
1250
1251 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
1252 for ln in fp:
1253 if ln.startswith('VERSION='):
1254 VERSION=ln.split()[1]
1255 if ln.startswith('ABIFLAGS='):
1256 ABIFLAGS=ln.split()[1]
1257 if ln.startswith('LDVERSION='):
1258 LDVERSION=ln.split()[1]
1259 fp.close()
1260
1261 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1262 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1263 config_suffix = '-' + LDVERSION
1264 else:
1265 config_suffix = '' # Python 2.x
1266
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001267 # We added some directories to the search path during the configure
1268 # phase. Remove those because those directories won't be there on
Ned Deilye1c9794952013-01-29 00:07:46 -08001269 # the end-users system. Also remove the directories from _sysconfigdata.py
1270 # (added in 3.3) if it exists.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001271
Ned Deilye6ef7022013-10-25 00:46:59 -07001272 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1273 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1274
Ned Deilye6ef7022013-10-25 00:46:59 -07001275 # fix Makefile
1276 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1277 fp = open(path, 'r')
1278 data = fp.read()
1279 fp.close()
1280
1281 for p in (include_path, lib_path):
1282 data = data.replace(" " + p, '')
1283 data = data.replace(p + " ", '')
1284
1285 fp = open(path, 'w')
1286 fp.write(data)
1287 fp.close()
1288
1289 # fix _sysconfigdata if it exists
1290 #
1291 # TODO: make this more robust! test_sysconfig_module of
1292 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1293 # the output from get_config_var in both sysconfig and
1294 # distutils.sysconfig is exactly the same for both CFLAGS and
1295 # LDFLAGS. The fixing up is now complicated by the pretty
1296 # printing in _sysconfigdata.py. Also, we are using the
1297 # pprint from the Python running the installer build which
1298 # may not cosmetically format the same as the pprint in the Python
1299 # being built (and which is used to originally generate
1300 # _sysconfigdata.py).
1301
1302 import pprint
1303 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1304 if os.path.exists(path):
Ned Deilye1c9794952013-01-29 00:07:46 -08001305 fp = open(path, 'r')
1306 data = fp.read()
1307 fp.close()
Ned Deilye6ef7022013-10-25 00:46:59 -07001308 # create build_time_vars dict
1309 exec(data)
1310 vars = {}
1311 for k, v in build_time_vars.items():
1312 if type(v) == type(''):
1313 for p in (include_path, lib_path):
1314 v = v.replace(' ' + p, '')
1315 v = v.replace(p + ' ', '')
1316 vars[k] = v
Ned Deilye1c9794952013-01-29 00:07:46 -08001317
Ned Deilye1c9794952013-01-29 00:07:46 -08001318 fp = open(path, 'w')
Ned Deilye6ef7022013-10-25 00:46:59 -07001319 # duplicated from sysconfig._generate_posix_vars()
1320 fp.write('# system configuration generated and used by'
1321 ' the sysconfig module\n')
1322 fp.write('build_time_vars = ')
1323 pprint.pprint(vars, stream=fp)
Ned Deilye1c9794952013-01-29 00:07:46 -08001324 fp.close()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001325
1326 # Add symlinks in /usr/local/bin, using relative links
1327 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1328 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1329 'Python.framework', 'Versions', version, 'bin')
1330 if os.path.exists(usr_local_bin):
1331 shutil.rmtree(usr_local_bin)
1332 os.makedirs(usr_local_bin)
1333 for fn in os.listdir(
1334 os.path.join(frmDir, 'Versions', version, 'bin')):
1335 os.symlink(os.path.join(to_framework, fn),
1336 os.path.join(usr_local_bin, fn))
1337
1338 os.chdir(curdir)
1339
Ned Deily53c460d2011-01-30 01:43:40 +00001340 if PYTHON_3:
Ezio Melotti6d0f0f22013-08-26 01:31:30 +03001341 # Remove the 'Current' link, that way we don't accidentally mess
Ned Deily53c460d2011-01-30 01:43:40 +00001342 # with an already installed version of python 2
1343 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1344 'Python.framework', 'Versions', 'Current'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001345
1346def patchFile(inPath, outPath):
1347 data = fileContents(inPath)
1348 data = data.replace('$FULL_VERSION', getFullVersion())
1349 data = data.replace('$VERSION', getVersion())
Ronald Oussoren508282e2009-03-30 19:34:51 +00001350 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussoren1e0a9982010-10-20 13:01:04 +00001351 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001352 data = data.replace('$INSTALL_SIZE', installSize())
Ned Deily62a86602014-12-09 23:45:13 -08001353 data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS))
Ronald Oussorenc5555542006-06-11 20:24:45 +00001354
1355 # This one is not handy as a template variable
1356 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deilye1c9794952013-01-29 00:07:46 -08001357 fp = open(outPath, 'w')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001358 fp.write(data)
1359 fp.close()
1360
1361def patchScript(inPath, outPath):
Ned Deily30101822014-11-14 18:53:59 -08001362 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001363 data = fileContents(inPath)
Ned Deily30101822014-11-14 18:53:59 -08001364 data = data.replace('@PYMAJOR@', str(major))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001365 data = data.replace('@PYVER@', getVersion())
Ned Deilye1c9794952013-01-29 00:07:46 -08001366 fp = open(outPath, 'w')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001367 fp.write(data)
1368 fp.close()
Ned Deilye1c9794952013-01-29 00:07:46 -08001369 os.chmod(outPath, STAT_0o755)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001370
1371
1372
1373def packageFromRecipe(targetDir, recipe):
1374 curdir = os.getcwd()
1375 try:
Ronald Oussorenaa560962006-11-07 15:53:38 +00001376 # The major version (such as 2.5) is included in the package name
1377 # because having two version of python installed at the same time is
Ronald Oussorenc5555542006-06-11 20:24:45 +00001378 # common.
1379 pkgname = '%s-%s'%(recipe['name'], getVersion())
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001380 srcdir = recipe.get('source')
1381 pkgroot = recipe.get('topdir', srcdir)
1382 postflight = recipe.get('postflight')
1383 readme = textwrap.dedent(recipe['readme'])
1384 isRequired = recipe.get('required', True)
1385
Ned Deilye1c9794952013-01-29 00:07:46 -08001386 print("- building package %s"%(pkgname,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001387
1388 # Substitute some variables
1389 textvars = dict(
1390 VER=getVersion(),
1391 FULLVER=getFullVersion(),
1392 )
1393 readme = readme % textvars
1394
1395 if pkgroot is not None:
1396 pkgroot = pkgroot % textvars
1397 else:
1398 pkgroot = '/'
1399
1400 if srcdir is not None:
1401 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1402 srcdir = srcdir % textvars
1403
1404 if postflight is not None:
1405 postflight = os.path.abspath(postflight)
1406
1407 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1408 os.makedirs(packageContents)
1409
1410 if srcdir is not None:
1411 os.chdir(srcdir)
1412 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1413 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1414 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1415
1416 fn = os.path.join(packageContents, 'PkgInfo')
1417 fp = open(fn, 'w')
1418 fp.write('pmkrpkg1')
1419 fp.close()
1420
1421 rsrcDir = os.path.join(packageContents, "Resources")
1422 os.mkdir(rsrcDir)
1423 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1424 fp.write(readme)
1425 fp.close()
1426
1427 if postflight is not None:
1428 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1429
1430 vers = getFullVersion()
Ned Deilye1c9794952013-01-29 00:07:46 -08001431 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001432 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001433 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1434 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1435 CFBundleName='Python.%s'%(pkgname,),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001436 CFBundleShortVersionString=vers,
1437 IFMajorVersion=major,
1438 IFMinorVersion=minor,
1439 IFPkgFormatVersion=0.10000000149011612,
1440 IFPkgFlagAllowBackRev=False,
1441 IFPkgFlagAuthorizationAction="RootAuthorization",
1442 IFPkgFlagDefaultLocation=pkgroot,
1443 IFPkgFlagFollowLinks=True,
1444 IFPkgFlagInstallFat=True,
1445 IFPkgFlagIsRequired=isRequired,
1446 IFPkgFlagOverwritePermissions=False,
1447 IFPkgFlagRelocatable=False,
1448 IFPkgFlagRestartAction="NoRestart",
1449 IFPkgFlagRootVolumeOnly=True,
1450 IFPkgFlagUpdateInstalledLangauges=False,
1451 )
1452 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1453
1454 pl = Plist(
1455 IFPkgDescriptionDescription=readme,
Ronald Oussoren508282e2009-03-30 19:34:51 +00001456 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001457 IFPkgDescriptionVersion=vers,
1458 )
1459 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1460
1461 finally:
1462 os.chdir(curdir)
1463
1464
1465def makeMpkgPlist(path):
1466
1467 vers = getFullVersion()
Ned Deilye1c9794952013-01-29 00:07:46 -08001468 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001469
1470 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001471 CFBundleGetInfoString="Python %s"%(vers,),
1472 CFBundleIdentifier='org.python.Python',
1473 CFBundleName='Python',
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001474 CFBundleShortVersionString=vers,
1475 IFMajorVersion=major,
1476 IFMinorVersion=minor,
1477 IFPkgFlagComponentDirectory="Contents/Packages",
1478 IFPkgFlagPackageList=[
1479 dict(
Ronald Oussorenc5555542006-06-11 20:24:45 +00001480 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Ronald Oussoren1a13cff2009-12-24 13:30:42 +00001481 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001482 )
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001483 for item in pkg_recipes()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001484 ],
1485 IFPkgFormatVersion=0.10000000149011612,
1486 IFPkgFlagBackgroundScaling="proportional",
1487 IFPkgFlagBackgroundAlignment="left",
Ronald Oussorenc5555542006-06-11 20:24:45 +00001488 IFPkgFlagAuthorizationAction="RootAuthorization",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001489 )
1490
1491 writePlist(pl, path)
1492
1493
1494def buildInstaller():
1495
1496 # Zap all compiled files
1497 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1498 for fn in filenames:
1499 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1500 os.unlink(os.path.join(dirpath, fn))
1501
1502 outdir = os.path.join(WORKDIR, 'installer')
1503 if os.path.exists(outdir):
1504 shutil.rmtree(outdir)
1505 os.mkdir(outdir)
1506
Ronald Oussoren508282e2009-03-30 19:34:51 +00001507 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001508 pkgcontents = os.path.join(pkgroot, 'Packages')
1509 os.makedirs(pkgcontents)
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001510 for recipe in pkg_recipes():
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001511 packageFromRecipe(pkgcontents, recipe)
1512
1513 rsrcDir = os.path.join(pkgroot, 'Resources')
1514
1515 fn = os.path.join(pkgroot, 'PkgInfo')
1516 fp = open(fn, 'w')
1517 fp.write('pmkrpkg1')
1518 fp.close()
1519
1520 os.mkdir(rsrcDir)
1521
1522 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1523 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001524 IFPkgDescriptionTitle="Python",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001525 IFPkgDescriptionVersion=getVersion(),
1526 )
1527
1528 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1529 for fn in os.listdir('resources'):
1530 if fn == '.svn': continue
1531 if fn.endswith('.jpg'):
1532 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1533 else:
1534 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1535
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001536
1537def installSize(clear=False, _saved=[]):
1538 if clear:
1539 del _saved[:]
1540 if not _saved:
1541 data = captureCommand("du -ks %s"%(
1542 shellQuote(os.path.join(WORKDIR, '_root'))))
1543 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1544 return _saved[0]
1545
1546
1547def buildDMG():
1548 """
Ronald Oussorenaa560962006-11-07 15:53:38 +00001549 Create DMG containing the rootDir.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001550 """
1551 outdir = os.path.join(WORKDIR, 'diskimage')
1552 if os.path.exists(outdir):
1553 shutil.rmtree(outdir)
1554
1555 imagepath = os.path.join(outdir,
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001556 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001557 if INCLUDE_TIMESTAMP:
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001558 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001559 imagepath = imagepath + '.dmg'
1560
1561 os.mkdir(outdir)
Ronald Oussoren508282e2009-03-30 19:34:51 +00001562 volname='Python %s'%(getFullVersion())
1563 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1564 shellQuote(volname),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001565 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren508282e2009-03-30 19:34:51 +00001566 shellQuote(imagepath + ".tmp.dmg" )))
1567
1568
1569 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1570 os.mkdir(os.path.join(WORKDIR, "mnt"))
1571 runCommand("hdiutil attach %s -mountroot %s"%(
1572 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1573
1574 # Custom icon for the DMG, shown when the DMG is mounted.
1575 shutil.copy("../Icons/Disk Image.icns",
1576 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deilye1c9794952013-01-29 00:07:46 -08001577 runCommand("SetFile -a C %s/"%(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001578 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1579
1580 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1581
1582 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1583 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1584 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1585 setIcon(imagepath, "../Icons/Disk Image.icns")
1586
1587 os.unlink(imagepath + ".tmp.dmg")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001588
1589 return imagepath
1590
1591
1592def setIcon(filePath, icnsPath):
1593 """
1594 Set the custom icon for the specified file or directory.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001595 """
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001596
Ronald Oussoren5d18cc62010-04-30 14:58:39 +00001597 dirPath = os.path.normpath(os.path.dirname(__file__))
1598 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001599 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1600 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1601 # to connections to the window server.
Ronald Oussoren5d18cc62010-04-30 14:58:39 +00001602 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1603 if not os.path.exists(appPath):
1604 os.makedirs(appPath)
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001605 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1606 shellQuote(toolPath), shellQuote(dirPath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001607
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001608 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1609 shellQuote(filePath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001610
1611def main():
1612 # First parse options and check if we can perform our work
1613 parseOptions()
1614 checkEnvironment()
1615
Ronald Oussoren508282e2009-03-30 19:34:51 +00001616 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Ronald Oussoren209d4c32009-09-29 19:34:13 +00001617 os.environ['CC'] = CC
Ned Deilye1c9794952013-01-29 00:07:46 -08001618 os.environ['CXX'] = CXX
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001619
1620 if os.path.exists(WORKDIR):
1621 shutil.rmtree(WORKDIR)
1622 os.mkdir(WORKDIR)
1623
Ronald Oussoren287128a2010-04-18 14:01:05 +00001624 os.environ['LC_ALL'] = 'C'
1625
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001626 # Then build third-party libraries such as sleepycat DB4.
1627 buildLibraries()
1628
1629 # Now build python itself
1630 buildPython()
Ronald Oussorene392b352009-03-31 13:20:45 +00001631
1632 # And then build the documentation
1633 # Remove the Deployment Target from the shell
1634 # environment, it's no longer needed and
1635 # an unexpected build target can cause problems
1636 # when Sphinx and its dependencies need to
1637 # be (re-)installed.
1638 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001639 buildPythonDocs()
Ronald Oussorene392b352009-03-31 13:20:45 +00001640
1641
1642 # Prepare the applications folder
Benjamin Petersonc3104762008-10-03 11:52:06 +00001643 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001644 getVersion(),))
Ned Deily62a86602014-12-09 23:45:13 -08001645 fn = os.path.join(folder, "License.rtf")
Ned Deily91d0a552014-12-13 00:33:29 -08001646 patchFile("resources/License.rtf", fn)
Ned Deily62a86602014-12-09 23:45:13 -08001647 fn = os.path.join(folder, "ReadMe.rtf")
Ned Deily91d0a552014-12-13 00:33:29 -08001648 patchFile("resources/ReadMe.rtf", fn)
Ned Deily62a86602014-12-09 23:45:13 -08001649 fn = os.path.join(folder, "Update Shell Profile.command")
1650 patchScript("scripts/postflight.patch-profile", fn)
Ned Deilye1c9794952013-01-29 00:07:46 -08001651 os.chmod(folder, STAT_0o755)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001652 setIcon(folder, "../Icons/Python Folder.icns")
1653
1654 # Create the installer
1655 buildInstaller()
1656
1657 # And copy the readme into the directory containing the installer
Ned Deily91d0a552014-12-13 00:33:29 -08001658 patchFile('resources/ReadMe.rtf',
1659 os.path.join(WORKDIR, 'installer', 'ReadMe.rtf'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001660
1661 # Ditto for the license file.
Ned Deily91d0a552014-12-13 00:33:29 -08001662 patchFile('resources/License.rtf',
1663 os.path.join(WORKDIR, 'installer', 'License.rtf'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001664
1665 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deilye1c9794952013-01-29 00:07:46 -08001666 fp.write("# BUILD INFO\n")
1667 fp.write("# Date: %s\n" % time.ctime())
1668 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001669 fp.close()
1670
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001671 # And copy it to a DMG
1672 buildDMG()
1673
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001674if __name__ == "__main__":
1675 main()