blob: 2304fef3ab04df83aa7fc80f6241a7887a84c166 [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
16requires an installed version of hg and a third-party version of
17Tcl/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 Deilyebd63dc2014-04-09 16:12:11 -070026(2.5 is needed for Python parser updates), hg, and for the documentation
27build 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 Deilyb5805b52016-03-08 01:07:44 -0500245 name="OpenSSL 1.0.2g",
246 url="https://www.openssl.org/source/openssl-1.0.2g.tar.gz",
247 checksum='f3c710c045cdee5fd114feb69feba7aa',
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")
580 except:
581 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 Deilyebd63dc2014-04-09 16:12:11 -0700666 # Ensure ws have access to hg and to sphinx-build.
667 # You may have to create links in /usr/bin for them.
668 runCommand('hg --version')
Ned Deily5ceffa12014-09-05 15:51:54 -0700669 runCommand('sphinx-build --version')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000670
Ronald Oussoren158ad592006-11-07 16:00:34 +0000671def parseOptions(args=None):
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000672 """
673 Parse arguments and update global settings.
674 """
Ronald Oussoren508282e2009-03-30 19:34:51 +0000675 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
Ned Deilye1c9794952013-01-29 00:07:46 -0800676 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily62a86602014-12-09 23:45:13 -0800677 global FW_VERSION_PREFIX
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000678
679 if args is None:
680 args = sys.argv[1:]
681
682 try:
683 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren508282e2009-03-30 19:34:51 +0000684 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
685 'dep-target=', 'universal-archs=', 'help' ])
Ned Deilye1c9794952013-01-29 00:07:46 -0800686 except getopt.GetoptError:
687 print(sys.exc_info()[1])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000688 sys.exit(1)
689
690 if args:
Ned Deilye1c9794952013-01-29 00:07:46 -0800691 print("Additional arguments")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000692 sys.exit(1)
693
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000694 deptarget = None
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000695 for k, v in options:
Ronald Oussoren508282e2009-03-30 19:34:51 +0000696 if k in ('-h', '-?', '--help'):
Ned Deilye1c9794952013-01-29 00:07:46 -0800697 print(USAGE)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000698 sys.exit(0)
699
700 elif k in ('-d', '--build-dir'):
701 WORKDIR=v
702
703 elif k in ('--third-party',):
704 DEPSRC=v
705
706 elif k in ('--sdk-path',):
707 SDKPATH=v
708
709 elif k in ('--src-dir',):
710 SRCDIR=v
711
Ronald Oussoren508282e2009-03-30 19:34:51 +0000712 elif k in ('--dep-target', ):
713 DEPTARGET=v
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000714 deptarget=v
Ronald Oussoren508282e2009-03-30 19:34:51 +0000715
716 elif k in ('--universal-archs', ):
717 if v in UNIVERSALOPTS:
718 UNIVERSALARCHS = v
719 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Ronald Oussorenc66ced32009-09-20 20:16:11 +0000720 if deptarget is None:
721 # Select alternate default deployment
722 # target
723 DEPTARGET = default_target_map.get(v, '10.3')
Ronald Oussoren508282e2009-03-30 19:34:51 +0000724 else:
Ned Deilye1c9794952013-01-29 00:07:46 -0800725 raise NotImplementedError(v)
Ronald Oussoren508282e2009-03-30 19:34:51 +0000726
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000727 else:
Ned Deilye1c9794952013-01-29 00:07:46 -0800728 raise NotImplementedError(k)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000729
730 SRCDIR=os.path.abspath(SRCDIR)
731 WORKDIR=os.path.abspath(WORKDIR)
732 SDKPATH=os.path.abspath(SDKPATH)
733 DEPSRC=os.path.abspath(DEPSRC)
734
Ned Deily1f70b872014-06-25 13:33:57 -0700735 CC, CXX = getTargetCompilers()
Ronald Oussoren209d4c32009-09-29 19:34:13 +0000736
Ned Deily62a86602014-12-09 23:45:13 -0800737 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
738
739 print("-- Settings:")
740 print(" * Source directory: %s" % SRCDIR)
741 print(" * Build directory: %s" % WORKDIR)
742 print(" * SDK location: %s" % SDKPATH)
743 print(" * Third-party source: %s" % DEPSRC)
744 print(" * Deployment target: %s" % DEPTARGET)
745 print(" * Universal archs: %s" % str(ARCHLIST))
746 print(" * C compiler: %s" % CC)
747 print(" * C++ compiler: %s" % CXX)
Ned Deilye1c9794952013-01-29 00:07:46 -0800748 print("")
Ned Deily62a86602014-12-09 23:45:13 -0800749 print(" -- Building a Python %s framework at patch level %s"
750 % (getVersion(), getFullVersion()))
751 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000752
753def extractArchive(builddir, archiveName):
754 """
755 Extract a source archive into 'builddir'. Returns the path of the
756 extracted archive.
757
758 XXX: This function assumes that archives contain a toplevel directory
759 that is has the same name as the basename of the archive. This is
Ned Deily0203a802013-10-25 00:40:07 -0700760 safe enough for almost anything we use. Unfortunately, it does not
761 work for current Tcl and Tk source releases where the basename of
762 the archive ends with "-src" but the uncompressed directory does not.
763 For now, just special case Tcl and Tk tar.gz downloads.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000764 """
765 curdir = os.getcwd()
766 try:
767 os.chdir(builddir)
768 if archiveName.endswith('.tar.gz'):
769 retval = os.path.basename(archiveName[:-7])
Ned Deily0203a802013-10-25 00:40:07 -0700770 if ((retval.startswith('tcl') or retval.startswith('tk'))
771 and retval.endswith('-src')):
772 retval = retval[:-4]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000773 if os.path.exists(retval):
774 shutil.rmtree(retval)
775 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
776
777 elif archiveName.endswith('.tar.bz2'):
778 retval = os.path.basename(archiveName[:-8])
779 if os.path.exists(retval):
780 shutil.rmtree(retval)
781 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
782
783 elif archiveName.endswith('.tar'):
784 retval = os.path.basename(archiveName[:-4])
785 if os.path.exists(retval):
786 shutil.rmtree(retval)
787 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
788
789 elif archiveName.endswith('.zip'):
790 retval = os.path.basename(archiveName[:-4])
791 if os.path.exists(retval):
792 shutil.rmtree(retval)
793 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
794
795 data = fp.read()
796 xit = fp.close()
797 if xit is not None:
798 sys.stdout.write(data)
Ned Deilye1c9794952013-01-29 00:07:46 -0800799 raise RuntimeError("Cannot extract %s"%(archiveName,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000800
801 return os.path.join(builddir, retval)
802
803 finally:
804 os.chdir(curdir)
805
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000806def downloadURL(url, fname):
807 """
808 Download the contents of the url into the file.
809 """
Ned Deilye1c9794952013-01-29 00:07:46 -0800810 fpIn = urllib_request.urlopen(url)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000811 fpOut = open(fname, 'wb')
812 block = fpIn.read(10240)
813 try:
814 while block:
815 fpOut.write(block)
816 block = fpIn.read(10240)
817 fpIn.close()
818 fpOut.close()
819 except:
820 try:
821 os.unlink(fname)
822 except:
823 pass
824
Ned Deilye1c9794952013-01-29 00:07:46 -0800825def verifyThirdPartyFile(url, checksum, fname):
826 """
827 Download file from url to filename fname if it does not already exist.
828 Abort if file contents does not match supplied md5 checksum.
829 """
830 name = os.path.basename(fname)
831 if os.path.exists(fname):
832 print("Using local copy of %s"%(name,))
833 else:
834 print("Did not find local copy of %s"%(name,))
835 print("Downloading %s"%(name,))
836 downloadURL(url, fname)
837 print("Archive for %s stored as %s"%(name, fname))
838 if os.system(
839 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
840 % (shellQuote(fname), checksum) ):
841 fatal('MD5 checksum mismatch for file %s' % fname)
842
Ned Deily62a86602014-12-09 23:45:13 -0800843def build_universal_openssl(basedir, archList):
844 """
845 Special case build recipe for universal build of openssl.
846
847 The upstream OpenSSL build system does not directly support
848 OS X universal builds. We need to build each architecture
849 separately then lipo them together into fat libraries.
850 """
851
Ned Deily0be4b1e2014-12-11 15:55:42 -0800852 # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4).
853 # If we are building on a 10.4.x or earlier system,
854 # unilaterally disable assembly code building to avoid the problem.
855 no_asm = int(platform.release().split(".")[0]) < 9
856
Ned Deily62a86602014-12-09 23:45:13 -0800857 def build_openssl_arch(archbase, arch):
858 "Build one architecture of openssl"
859 arch_opts = {
860 "i386": ["darwin-i386-cc"],
861 "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
862 "ppc": ["darwin-ppc-cc"],
863 "ppc64": ["darwin64-ppc-cc"],
864 }
865 configure_opts = [
866 "no-krb5",
867 "no-idea",
868 "no-mdc2",
869 "no-rc5",
870 "no-zlib",
871 "enable-tlsext",
872 "no-ssl2",
873 "no-ssl3",
874 "no-ssl3-method",
875 # "enable-unit-test",
876 "shared",
877 "--install_prefix=%s"%shellQuote(archbase),
878 "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX),
879 "--openssldir=/System/Library/OpenSSL",
880 ]
Ned Deily0be4b1e2014-12-11 15:55:42 -0800881 if no_asm:
882 configure_opts.append("no-asm")
Ned Deily62a86602014-12-09 23:45:13 -0800883 runCommand(" ".join(["perl", "Configure"]
884 + arch_opts[arch] + configure_opts))
885 runCommand("make depend OSX_SDK=%s" % SDKPATH)
886 runCommand("make all OSX_SDK=%s" % SDKPATH)
887 runCommand("make install_sw OSX_SDK=%s" % SDKPATH)
888 # runCommand("make test")
889 return
890
891 srcdir = os.getcwd()
892 universalbase = os.path.join(srcdir, "..",
893 os.path.basename(srcdir) + "-universal")
894 os.mkdir(universalbase)
895 archbasefws = []
896 for arch in archList:
897 # fresh copy of the source tree
898 archsrc = os.path.join(universalbase, arch, "src")
899 shutil.copytree(srcdir, archsrc, symlinks=True)
900 # install base for this arch
901 archbase = os.path.join(universalbase, arch, "root")
902 os.mkdir(archbase)
903 # Python framework base within install_prefix:
904 # the build will install into this framework..
905 # This is to ensure that the resulting shared libs have
906 # the desired real install paths built into them.
907 archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX)
908
909 # build one architecture
910 os.chdir(archsrc)
911 build_openssl_arch(archbase, arch)
912 os.chdir(srcdir)
913 archbasefws.append(archbasefw)
914
915 # copy arch-independent files from last build into the basedir framework
916 basefw = os.path.join(basedir, *FW_VERSION_PREFIX)
917 shutil.copytree(
918 os.path.join(archbasefw, "include", "openssl"),
919 os.path.join(basefw, "include", "openssl")
920 )
921
922 shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"),
923 "SHLIB_VERSION_NUMBER")
924 # e.g. -> "1.0.0"
925 libcrypto = "libcrypto.dylib"
926 libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".")
927 # e.g. -> "libcrypto.1.0.0.dylib"
928 libssl = "libssl.dylib"
929 libssl_versioned = libssl.replace(".", "."+shlib_version_number+".")
930 # e.g. -> "libssl.1.0.0.dylib"
931
932 try:
933 os.mkdir(os.path.join(basefw, "lib"))
934 except OSError:
935 pass
936
937 # merge the individual arch-dependent shared libs into a fat shared lib
938 archbasefws.insert(0, basefw)
939 for (lib_unversioned, lib_versioned) in [
940 (libcrypto, libcrypto_versioned),
941 (libssl, libssl_versioned)
942 ]:
943 runCommand("lipo -create -output " +
944 " ".join(shellQuote(
945 os.path.join(fw, "lib", lib_versioned))
946 for fw in archbasefws))
947 # and create an unversioned symlink of it
948 os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned))
949
950 # Create links in the temp include and lib dirs that will be injected
951 # into the Python build so that setup.py can find them while building
952 # and the versioned links so that the setup.py post-build import test
953 # does not fail.
954 relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX)
955 for fn in [
956 ["include", "openssl"],
957 ["lib", libcrypto],
958 ["lib", libssl],
959 ["lib", libcrypto_versioned],
960 ["lib", libssl_versioned],
961 ]:
962 os.symlink(
963 os.path.join(relative_path, *fn),
964 os.path.join(basedir, "usr", "local", *fn)
965 )
966
967 return
968
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000969def buildRecipe(recipe, basedir, archList):
970 """
971 Build software using a recipe. This function does the
972 'configure;make;make install' dance for C software, with a possibility
973 to customize this process, basically a poor-mans DarwinPorts.
974 """
975 curdir = os.getcwd()
976
977 name = recipe['name']
Ned Deily62a86602014-12-09 23:45:13 -0800978 THIRD_PARTY_LIBS.append(name)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000979 url = recipe['url']
980 configure = recipe.get('configure', './configure')
Ned Deily62a86602014-12-09 23:45:13 -0800981 buildrecipe = recipe.get('buildrecipe', None)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000982 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
983 shellQuote(basedir)))
984
985 archiveName = os.path.split(url)[-1]
986 sourceArchive = os.path.join(DEPSRC, archiveName)
987
988 if not os.path.exists(DEPSRC):
989 os.mkdir(DEPSRC)
990
Ned Deilye1c9794952013-01-29 00:07:46 -0800991 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
992 print("Extracting archive for %s"%(name,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000993 buildDir=os.path.join(WORKDIR, '_bld')
994 if not os.path.exists(buildDir):
995 os.mkdir(buildDir)
996
997 workDir = extractArchive(buildDir, sourceArchive)
998 os.chdir(workDir)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +0000999
Ned Deilye1c9794952013-01-29 00:07:46 -08001000 for patch in recipe.get('patches', ()):
1001 if isinstance(patch, tuple):
1002 url, checksum = patch
1003 fn = os.path.join(DEPSRC, os.path.basename(url))
1004 verifyThirdPartyFile(url, checksum, fn)
1005 else:
1006 # patch is a file in the source directory
1007 fn = os.path.join(curdir, patch)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001008 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
1009 shellQuote(fn),))
1010
Ned Deilye1c9794952013-01-29 00:07:46 -08001011 for patchscript in recipe.get('patchscripts', ()):
1012 if isinstance(patchscript, tuple):
1013 url, checksum = patchscript
1014 fn = os.path.join(DEPSRC, os.path.basename(url))
1015 verifyThirdPartyFile(url, checksum, fn)
1016 else:
1017 # patch is a file in the source directory
1018 fn = os.path.join(curdir, patchscript)
1019 if fn.endswith('.bz2'):
1020 runCommand('bunzip2 -fk %s' % shellQuote(fn))
1021 fn = fn[:-4]
1022 runCommand('sh %s' % shellQuote(fn))
1023 os.unlink(fn)
1024
Ned Deilya6cbff02013-10-27 19:47:23 -07001025 if 'buildDir' in recipe:
1026 os.chdir(recipe['buildDir'])
1027
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001028 if configure is not None:
1029 configure_args = [
1030 "--prefix=/usr/local",
1031 "--enable-static",
1032 "--disable-shared",
1033 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
1034 ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001035
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001036 if 'configure_pre' in recipe:
1037 args = list(recipe['configure_pre'])
1038 if '--disable-static' in args:
1039 configure_args.remove('--enable-static')
1040 if '--enable-shared' in args:
1041 configure_args.remove('--disable-shared')
1042 configure_args.extend(args)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001043
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001044 if recipe.get('useLDFlags', 1):
1045 configure_args.extend([
Ned Deilye1c9794952013-01-29 00:07:46 -08001046 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
1047 "-I%s/usr/local/include"%(
1048 recipe.get('extra_cflags', ''),
1049 DEPTARGET,
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001050 ' -arch '.join(archList),
1051 shellQuote(SDKPATH)[1:-1],
1052 shellQuote(basedir)[1:-1],),
Ned Deilyc177b1c2014-04-09 16:13:46 -07001053 "LDFLAGS=-mmacosx-version-min=%s -isysroot %s -L%s/usr/local/lib -arch %s"%(
Ned Deilye1c9794952013-01-29 00:07:46 -08001054 DEPTARGET,
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001055 shellQuote(SDKPATH)[1:-1],
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001056 shellQuote(basedir)[1:-1],
1057 ' -arch '.join(archList)),
1058 ])
1059 else:
1060 configure_args.extend([
Ned Deilye1c9794952013-01-29 00:07:46 -08001061 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
1062 "-I%s/usr/local/include"%(
1063 recipe.get('extra_cflags', ''),
1064 DEPTARGET,
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001065 ' -arch '.join(archList),
1066 shellQuote(SDKPATH)[1:-1],
1067 shellQuote(basedir)[1:-1],),
1068 ])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001069
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001070 if 'configure_post' in recipe:
Ned Deilye1c9794952013-01-29 00:07:46 -08001071 configure_args = configure_args + list(recipe['configure_post'])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001072
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001073 configure_args.insert(0, configure)
1074 configure_args = [ shellQuote(a) for a in configure_args ]
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001075
Ned Deilye1c9794952013-01-29 00:07:46 -08001076 print("Running configure for %s"%(name,))
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001077 runCommand(' '.join(configure_args) + ' 2>&1')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001078
Ned Deily62a86602014-12-09 23:45:13 -08001079 if buildrecipe is not None:
1080 # call special-case build recipe, e.g. for openssl
1081 buildrecipe(basedir, archList)
1082
1083 if install is not None:
1084 print("Running install for %s"%(name,))
1085 runCommand('{ ' + install + ' ;} 2>&1')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001086
Ned Deilye1c9794952013-01-29 00:07:46 -08001087 print("Done %s"%(name,))
1088 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001089
1090 os.chdir(curdir)
1091
1092def buildLibraries():
1093 """
1094 Build our dependencies into $WORKDIR/libraries/usr/local
1095 """
Ned Deilye1c9794952013-01-29 00:07:46 -08001096 print("")
1097 print("Building required libraries")
1098 print("")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001099 universal = os.path.join(WORKDIR, 'libraries')
1100 os.mkdir(universal)
1101 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
1102 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
1103
Ronald Oussoren508282e2009-03-30 19:34:51 +00001104 for recipe in library_recipes():
Ronald Oussoren9b8b6192006-06-27 12:53:52 +00001105 buildRecipe(recipe, universal, ARCHLIST)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001106
1107
1108
1109def buildPythonDocs():
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001110 # This stores the documentation as Resources/English.lproj/Documentation
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001111 # inside the framwork. pydoc and IDLE will pick it up there.
Ned Deilye1c9794952013-01-29 00:07:46 -08001112 print("Install python documentation")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001113 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001114 buildDir = os.path.join('../../Doc')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001115 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001116 curDir = os.getcwd()
1117 os.chdir(buildDir)
Ned Deily5ceffa12014-09-05 15:51:54 -07001118 # The Doc build changed for 3.4 (technically, for 3.4.1) and for 2.7.9
1119 runCommand('make clean')
1120 # Assume sphinx-build is on our PATH, checked in checkEnvironment
1121 runCommand('make html')
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001122 os.chdir(curDir)
1123 if not os.path.exists(docdir):
1124 os.mkdir(docdir)
Ronald Oussoren209d4c32009-09-29 19:34:13 +00001125 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001126
1127
1128def buildPython():
Ned Deilye1c9794952013-01-29 00:07:46 -08001129 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001130
1131 buildDir = os.path.join(WORKDIR, '_bld', 'python')
1132 rootDir = os.path.join(WORKDIR, '_root')
1133
1134 if os.path.exists(buildDir):
1135 shutil.rmtree(buildDir)
1136 if os.path.exists(rootDir):
1137 shutil.rmtree(rootDir)
Ned Deily53c460d2011-01-30 01:43:40 +00001138 os.makedirs(buildDir)
1139 os.makedirs(rootDir)
1140 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001141 curdir = os.getcwd()
1142 os.chdir(buildDir)
1143
1144 # Not sure if this is still needed, the original build script
1145 # claims that parts of the install assume python.exe exists.
1146 os.symlink('python', os.path.join(buildDir, 'python.exe'))
1147
1148 # Extract the version from the configure file, needed to calculate
1149 # several paths.
1150 version = getVersion()
1151
Ronald Oussoren008af852009-03-30 20:02:08 +00001152 # Since the extra libs are not in their installed framework location
1153 # during the build, augment the library path so that the interpreter
1154 # will find them during its extension import sanity checks.
1155 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
1156 'libraries', 'usr', 'local', 'lib')
Ned Deilye1c9794952013-01-29 00:07:46 -08001157 print("Running configure...")
Ronald Oussoren508282e2009-03-30 19:34:51 +00001158 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
1159 "--with-universal-archs=%s "
Ned Deily53c460d2011-01-30 01:43:40 +00001160 "%s "
Ned Deilyebd63dc2014-04-09 16:12:11 -07001161 "%s "
Ronald Oussoren508282e2009-03-30 19:34:51 +00001162 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deilyf84b5312013-10-25 00:44:46 -07001163 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001164 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
1165 UNIVERSALARCHS,
Ned Deily53c460d2011-01-30 01:43:40 +00001166 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deily30101822014-11-14 18:53:59 -08001167 (' ', '--without-ensurepip ')[PYTHON_3],
Ronald Oussoren508282e2009-03-30 19:34:51 +00001168 shellQuote(WORKDIR)[1:-1],
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001169 shellQuote(WORKDIR)[1:-1]))
1170
Ned Deilyfbb60d52014-05-22 15:27:01 -07001171 print("Running make touch")
1172 runCommand("make touch")
1173
Ned Deilye1c9794952013-01-29 00:07:46 -08001174 print("Running make")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001175 runCommand("make")
1176
Ned Deilye1c9794952013-01-29 00:07:46 -08001177 print("Running make install")
Ned Deily53c460d2011-01-30 01:43:40 +00001178 runCommand("make install DESTDIR=%s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001179 shellQuote(rootDir)))
1180
Ned Deilye1c9794952013-01-29 00:07:46 -08001181 print("Running make frameworkinstallextras")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001182 runCommand("make frameworkinstallextras DESTDIR=%s"%(
1183 shellQuote(rootDir)))
1184
Ronald Oussoren008af852009-03-30 20:02:08 +00001185 del os.environ['DYLD_LIBRARY_PATH']
Ned Deilye1c9794952013-01-29 00:07:46 -08001186 print("Copying required shared libraries")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001187 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
1188 runCommand("mv %s/* %s"%(
1189 shellQuote(os.path.join(
1190 WORKDIR, 'libraries', 'Library', 'Frameworks',
1191 'Python.framework', 'Versions', getVersion(),
1192 'lib')),
1193 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
1194 'Python.framework', 'Versions', getVersion(),
1195 'lib'))))
1196
Ned Deily70f213a2013-10-26 03:16:06 -07001197 path_to_lib = os.path.join(rootDir, 'Library', 'Frameworks',
1198 'Python.framework', 'Versions',
1199 version, 'lib', 'python%s'%(version,))
1200
Ned Deilye1c9794952013-01-29 00:07:46 -08001201 print("Fix file modes")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001202 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Ronald Oussoren74d3eef2006-10-10 07:55:06 +00001203 gid = grp.getgrnam('admin').gr_gid
1204
Ned Deilye1c9794952013-01-29 00:07:46 -08001205 shared_lib_error = False
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001206 for dirpath, dirnames, filenames in os.walk(frmDir):
1207 for dn in dirnames:
Ned Deilye1c9794952013-01-29 00:07:46 -08001208 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +00001209 os.chown(os.path.join(dirpath, dn), -1, gid)
Tim Petersef3f32f2006-10-18 05:09:12 +00001210
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001211 for fn in filenames:
1212 if os.path.islink(fn):
1213 continue
1214
1215 # "chmod g+w $fn"
1216 p = os.path.join(dirpath, fn)
1217 st = os.stat(p)
Ronald Oussoren74d3eef2006-10-10 07:55:06 +00001218 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1219 os.chown(p, -1, gid)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001220
Ned Deilye1c9794952013-01-29 00:07:46 -08001221 if fn in EXPECTED_SHARED_LIBS:
1222 # check to see that this file was linked with the
1223 # expected library path and version
1224 data = captureCommand("otool -L %s" % shellQuote(p))
1225 for sl in EXPECTED_SHARED_LIBS[fn]:
1226 if ("\t%s " % sl) not in data:
1227 print("Expected shared lib %s was not linked with %s"
1228 % (sl, p))
1229 shared_lib_error = True
1230
1231 if shared_lib_error:
1232 fatal("Unexpected shared library errors.")
1233
Ned Deily53c460d2011-01-30 01:43:40 +00001234 if PYTHON_3:
1235 LDVERSION=None
1236 VERSION=None
1237 ABIFLAGS=None
1238
1239 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
1240 for ln in fp:
1241 if ln.startswith('VERSION='):
1242 VERSION=ln.split()[1]
1243 if ln.startswith('ABIFLAGS='):
1244 ABIFLAGS=ln.split()[1]
1245 if ln.startswith('LDVERSION='):
1246 LDVERSION=ln.split()[1]
1247 fp.close()
1248
1249 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1250 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1251 config_suffix = '-' + LDVERSION
1252 else:
1253 config_suffix = '' # Python 2.x
1254
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001255 # We added some directories to the search path during the configure
1256 # phase. Remove those because those directories won't be there on
Ned Deilye1c9794952013-01-29 00:07:46 -08001257 # the end-users system. Also remove the directories from _sysconfigdata.py
1258 # (added in 3.3) if it exists.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001259
Ned Deilye6ef7022013-10-25 00:46:59 -07001260 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1261 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1262
Ned Deilye6ef7022013-10-25 00:46:59 -07001263 # fix Makefile
1264 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1265 fp = open(path, 'r')
1266 data = fp.read()
1267 fp.close()
1268
1269 for p in (include_path, lib_path):
1270 data = data.replace(" " + p, '')
1271 data = data.replace(p + " ", '')
1272
1273 fp = open(path, 'w')
1274 fp.write(data)
1275 fp.close()
1276
1277 # fix _sysconfigdata if it exists
1278 #
1279 # TODO: make this more robust! test_sysconfig_module of
1280 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1281 # the output from get_config_var in both sysconfig and
1282 # distutils.sysconfig is exactly the same for both CFLAGS and
1283 # LDFLAGS. The fixing up is now complicated by the pretty
1284 # printing in _sysconfigdata.py. Also, we are using the
1285 # pprint from the Python running the installer build which
1286 # may not cosmetically format the same as the pprint in the Python
1287 # being built (and which is used to originally generate
1288 # _sysconfigdata.py).
1289
1290 import pprint
1291 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1292 if os.path.exists(path):
Ned Deilye1c9794952013-01-29 00:07:46 -08001293 fp = open(path, 'r')
1294 data = fp.read()
1295 fp.close()
Ned Deilye6ef7022013-10-25 00:46:59 -07001296 # create build_time_vars dict
1297 exec(data)
1298 vars = {}
1299 for k, v in build_time_vars.items():
1300 if type(v) == type(''):
1301 for p in (include_path, lib_path):
1302 v = v.replace(' ' + p, '')
1303 v = v.replace(p + ' ', '')
1304 vars[k] = v
Ned Deilye1c9794952013-01-29 00:07:46 -08001305
Ned Deilye1c9794952013-01-29 00:07:46 -08001306 fp = open(path, 'w')
Ned Deilye6ef7022013-10-25 00:46:59 -07001307 # duplicated from sysconfig._generate_posix_vars()
1308 fp.write('# system configuration generated and used by'
1309 ' the sysconfig module\n')
1310 fp.write('build_time_vars = ')
1311 pprint.pprint(vars, stream=fp)
Ned Deilye1c9794952013-01-29 00:07:46 -08001312 fp.close()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001313
1314 # Add symlinks in /usr/local/bin, using relative links
1315 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1316 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1317 'Python.framework', 'Versions', version, 'bin')
1318 if os.path.exists(usr_local_bin):
1319 shutil.rmtree(usr_local_bin)
1320 os.makedirs(usr_local_bin)
1321 for fn in os.listdir(
1322 os.path.join(frmDir, 'Versions', version, 'bin')):
1323 os.symlink(os.path.join(to_framework, fn),
1324 os.path.join(usr_local_bin, fn))
1325
1326 os.chdir(curdir)
1327
Ned Deily53c460d2011-01-30 01:43:40 +00001328 if PYTHON_3:
Ezio Melotti6d0f0f22013-08-26 01:31:30 +03001329 # Remove the 'Current' link, that way we don't accidentally mess
Ned Deily53c460d2011-01-30 01:43:40 +00001330 # with an already installed version of python 2
1331 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1332 'Python.framework', 'Versions', 'Current'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001333
1334def patchFile(inPath, outPath):
1335 data = fileContents(inPath)
1336 data = data.replace('$FULL_VERSION', getFullVersion())
1337 data = data.replace('$VERSION', getVersion())
Ronald Oussoren508282e2009-03-30 19:34:51 +00001338 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussoren1e0a9982010-10-20 13:01:04 +00001339 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001340 data = data.replace('$INSTALL_SIZE', installSize())
Ned Deily62a86602014-12-09 23:45:13 -08001341 data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS))
Ronald Oussorenc5555542006-06-11 20:24:45 +00001342
1343 # This one is not handy as a template variable
1344 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deilye1c9794952013-01-29 00:07:46 -08001345 fp = open(outPath, 'w')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001346 fp.write(data)
1347 fp.close()
1348
1349def patchScript(inPath, outPath):
Ned Deily30101822014-11-14 18:53:59 -08001350 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001351 data = fileContents(inPath)
Ned Deily30101822014-11-14 18:53:59 -08001352 data = data.replace('@PYMAJOR@', str(major))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001353 data = data.replace('@PYVER@', getVersion())
Ned Deilye1c9794952013-01-29 00:07:46 -08001354 fp = open(outPath, 'w')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001355 fp.write(data)
1356 fp.close()
Ned Deilye1c9794952013-01-29 00:07:46 -08001357 os.chmod(outPath, STAT_0o755)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001358
1359
1360
1361def packageFromRecipe(targetDir, recipe):
1362 curdir = os.getcwd()
1363 try:
Ronald Oussorenaa560962006-11-07 15:53:38 +00001364 # The major version (such as 2.5) is included in the package name
1365 # because having two version of python installed at the same time is
Ronald Oussorenc5555542006-06-11 20:24:45 +00001366 # common.
1367 pkgname = '%s-%s'%(recipe['name'], getVersion())
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001368 srcdir = recipe.get('source')
1369 pkgroot = recipe.get('topdir', srcdir)
1370 postflight = recipe.get('postflight')
1371 readme = textwrap.dedent(recipe['readme'])
1372 isRequired = recipe.get('required', True)
1373
Ned Deilye1c9794952013-01-29 00:07:46 -08001374 print("- building package %s"%(pkgname,))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001375
1376 # Substitute some variables
1377 textvars = dict(
1378 VER=getVersion(),
1379 FULLVER=getFullVersion(),
1380 )
1381 readme = readme % textvars
1382
1383 if pkgroot is not None:
1384 pkgroot = pkgroot % textvars
1385 else:
1386 pkgroot = '/'
1387
1388 if srcdir is not None:
1389 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1390 srcdir = srcdir % textvars
1391
1392 if postflight is not None:
1393 postflight = os.path.abspath(postflight)
1394
1395 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1396 os.makedirs(packageContents)
1397
1398 if srcdir is not None:
1399 os.chdir(srcdir)
1400 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1401 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1402 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1403
1404 fn = os.path.join(packageContents, 'PkgInfo')
1405 fp = open(fn, 'w')
1406 fp.write('pmkrpkg1')
1407 fp.close()
1408
1409 rsrcDir = os.path.join(packageContents, "Resources")
1410 os.mkdir(rsrcDir)
1411 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1412 fp.write(readme)
1413 fp.close()
1414
1415 if postflight is not None:
1416 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1417
1418 vers = getFullVersion()
Ned Deilye1c9794952013-01-29 00:07:46 -08001419 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001420 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001421 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1422 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1423 CFBundleName='Python.%s'%(pkgname,),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001424 CFBundleShortVersionString=vers,
1425 IFMajorVersion=major,
1426 IFMinorVersion=minor,
1427 IFPkgFormatVersion=0.10000000149011612,
1428 IFPkgFlagAllowBackRev=False,
1429 IFPkgFlagAuthorizationAction="RootAuthorization",
1430 IFPkgFlagDefaultLocation=pkgroot,
1431 IFPkgFlagFollowLinks=True,
1432 IFPkgFlagInstallFat=True,
1433 IFPkgFlagIsRequired=isRequired,
1434 IFPkgFlagOverwritePermissions=False,
1435 IFPkgFlagRelocatable=False,
1436 IFPkgFlagRestartAction="NoRestart",
1437 IFPkgFlagRootVolumeOnly=True,
1438 IFPkgFlagUpdateInstalledLangauges=False,
1439 )
1440 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1441
1442 pl = Plist(
1443 IFPkgDescriptionDescription=readme,
Ronald Oussoren508282e2009-03-30 19:34:51 +00001444 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001445 IFPkgDescriptionVersion=vers,
1446 )
1447 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1448
1449 finally:
1450 os.chdir(curdir)
1451
1452
1453def makeMpkgPlist(path):
1454
1455 vers = getFullVersion()
Ned Deilye1c9794952013-01-29 00:07:46 -08001456 major, minor = getVersionMajorMinor()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001457
1458 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001459 CFBundleGetInfoString="Python %s"%(vers,),
1460 CFBundleIdentifier='org.python.Python',
1461 CFBundleName='Python',
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001462 CFBundleShortVersionString=vers,
1463 IFMajorVersion=major,
1464 IFMinorVersion=minor,
1465 IFPkgFlagComponentDirectory="Contents/Packages",
1466 IFPkgFlagPackageList=[
1467 dict(
Ronald Oussorenc5555542006-06-11 20:24:45 +00001468 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Ronald Oussoren1a13cff2009-12-24 13:30:42 +00001469 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001470 )
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001471 for item in pkg_recipes()
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001472 ],
1473 IFPkgFormatVersion=0.10000000149011612,
1474 IFPkgFlagBackgroundScaling="proportional",
1475 IFPkgFlagBackgroundAlignment="left",
Ronald Oussorenc5555542006-06-11 20:24:45 +00001476 IFPkgFlagAuthorizationAction="RootAuthorization",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001477 )
1478
1479 writePlist(pl, path)
1480
1481
1482def buildInstaller():
1483
1484 # Zap all compiled files
1485 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1486 for fn in filenames:
1487 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1488 os.unlink(os.path.join(dirpath, fn))
1489
1490 outdir = os.path.join(WORKDIR, 'installer')
1491 if os.path.exists(outdir):
1492 shutil.rmtree(outdir)
1493 os.mkdir(outdir)
1494
Ronald Oussoren508282e2009-03-30 19:34:51 +00001495 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001496 pkgcontents = os.path.join(pkgroot, 'Packages')
1497 os.makedirs(pkgcontents)
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001498 for recipe in pkg_recipes():
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001499 packageFromRecipe(pkgcontents, recipe)
1500
1501 rsrcDir = os.path.join(pkgroot, 'Resources')
1502
1503 fn = os.path.join(pkgroot, 'PkgInfo')
1504 fp = open(fn, 'w')
1505 fp.write('pmkrpkg1')
1506 fp.close()
1507
1508 os.mkdir(rsrcDir)
1509
1510 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1511 pl = Plist(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001512 IFPkgDescriptionTitle="Python",
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001513 IFPkgDescriptionVersion=getVersion(),
1514 )
1515
1516 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1517 for fn in os.listdir('resources'):
1518 if fn == '.svn': continue
1519 if fn.endswith('.jpg'):
1520 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1521 else:
1522 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1523
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001524
1525def installSize(clear=False, _saved=[]):
1526 if clear:
1527 del _saved[:]
1528 if not _saved:
1529 data = captureCommand("du -ks %s"%(
1530 shellQuote(os.path.join(WORKDIR, '_root'))))
1531 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1532 return _saved[0]
1533
1534
1535def buildDMG():
1536 """
Ronald Oussorenaa560962006-11-07 15:53:38 +00001537 Create DMG containing the rootDir.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001538 """
1539 outdir = os.path.join(WORKDIR, 'diskimage')
1540 if os.path.exists(outdir):
1541 shutil.rmtree(outdir)
1542
1543 imagepath = os.path.join(outdir,
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001544 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001545 if INCLUDE_TIMESTAMP:
Ronald Oussorenc66ced32009-09-20 20:16:11 +00001546 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001547 imagepath = imagepath + '.dmg'
1548
1549 os.mkdir(outdir)
Ronald Oussoren508282e2009-03-30 19:34:51 +00001550 volname='Python %s'%(getFullVersion())
1551 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1552 shellQuote(volname),
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001553 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren508282e2009-03-30 19:34:51 +00001554 shellQuote(imagepath + ".tmp.dmg" )))
1555
1556
1557 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1558 os.mkdir(os.path.join(WORKDIR, "mnt"))
1559 runCommand("hdiutil attach %s -mountroot %s"%(
1560 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1561
1562 # Custom icon for the DMG, shown when the DMG is mounted.
1563 shutil.copy("../Icons/Disk Image.icns",
1564 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deilye1c9794952013-01-29 00:07:46 -08001565 runCommand("SetFile -a C %s/"%(
Ronald Oussoren508282e2009-03-30 19:34:51 +00001566 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1567
1568 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1569
1570 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1571 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1572 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1573 setIcon(imagepath, "../Icons/Disk Image.icns")
1574
1575 os.unlink(imagepath + ".tmp.dmg")
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001576
1577 return imagepath
1578
1579
1580def setIcon(filePath, icnsPath):
1581 """
1582 Set the custom icon for the specified file or directory.
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001583 """
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001584
Ronald Oussoren5d18cc62010-04-30 14:58:39 +00001585 dirPath = os.path.normpath(os.path.dirname(__file__))
1586 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001587 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1588 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1589 # to connections to the window server.
Ronald Oussoren5d18cc62010-04-30 14:58:39 +00001590 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1591 if not os.path.exists(appPath):
1592 os.makedirs(appPath)
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001593 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1594 shellQuote(toolPath), shellQuote(dirPath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001595
Ronald Oussoren648a4fe2009-03-30 17:15:29 +00001596 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1597 shellQuote(filePath)))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001598
1599def main():
1600 # First parse options and check if we can perform our work
1601 parseOptions()
1602 checkEnvironment()
1603
Ronald Oussoren508282e2009-03-30 19:34:51 +00001604 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Ronald Oussoren209d4c32009-09-29 19:34:13 +00001605 os.environ['CC'] = CC
Ned Deilye1c9794952013-01-29 00:07:46 -08001606 os.environ['CXX'] = CXX
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001607
1608 if os.path.exists(WORKDIR):
1609 shutil.rmtree(WORKDIR)
1610 os.mkdir(WORKDIR)
1611
Ronald Oussoren287128a2010-04-18 14:01:05 +00001612 os.environ['LC_ALL'] = 'C'
1613
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001614 # Then build third-party libraries such as sleepycat DB4.
1615 buildLibraries()
1616
1617 # Now build python itself
1618 buildPython()
Ronald Oussorene392b352009-03-31 13:20:45 +00001619
1620 # And then build the documentation
1621 # Remove the Deployment Target from the shell
1622 # environment, it's no longer needed and
1623 # an unexpected build target can cause problems
1624 # when Sphinx and its dependencies need to
1625 # be (re-)installed.
1626 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001627 buildPythonDocs()
Ronald Oussorene392b352009-03-31 13:20:45 +00001628
1629
1630 # Prepare the applications folder
Benjamin Petersonc3104762008-10-03 11:52:06 +00001631 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001632 getVersion(),))
Ned Deily62a86602014-12-09 23:45:13 -08001633 fn = os.path.join(folder, "License.rtf")
Ned Deily91d0a552014-12-13 00:33:29 -08001634 patchFile("resources/License.rtf", fn)
Ned Deily62a86602014-12-09 23:45:13 -08001635 fn = os.path.join(folder, "ReadMe.rtf")
Ned Deily91d0a552014-12-13 00:33:29 -08001636 patchFile("resources/ReadMe.rtf", fn)
Ned Deily62a86602014-12-09 23:45:13 -08001637 fn = os.path.join(folder, "Update Shell Profile.command")
1638 patchScript("scripts/postflight.patch-profile", fn)
Ned Deilye1c9794952013-01-29 00:07:46 -08001639 os.chmod(folder, STAT_0o755)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001640 setIcon(folder, "../Icons/Python Folder.icns")
1641
1642 # Create the installer
1643 buildInstaller()
1644
1645 # And copy the readme into the directory containing the installer
Ned Deily91d0a552014-12-13 00:33:29 -08001646 patchFile('resources/ReadMe.rtf',
1647 os.path.join(WORKDIR, 'installer', 'ReadMe.rtf'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001648
1649 # Ditto for the license file.
Ned Deily91d0a552014-12-13 00:33:29 -08001650 patchFile('resources/License.rtf',
1651 os.path.join(WORKDIR, 'installer', 'License.rtf'))
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001652
1653 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deilye1c9794952013-01-29 00:07:46 -08001654 fp.write("# BUILD INFO\n")
1655 fp.write("# Date: %s\n" % time.ctime())
1656 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001657 fp.close()
1658
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001659 # And copy it to a DMG
1660 buildDMG()
1661
Ronald Oussoren0e5b70d2006-06-07 18:58:42 +00001662if __name__ == "__main__":
1663 main()