blob: 4e15f2dc8a0c41498c83aa2fc9211d24d584df42 [file] [log] [blame]
Ned Deily4a96a372013-01-29 00:08:32 -08001#!/usr/bin/env python
Thomas Wouters477c8d52006-05-27 19:21:47 +00002"""
Ned Deilye59e4c52011-01-29 18:56:28 +00003This script is used to build "official" universal installers on Mac OS X.
Ned Deily4a96a372013-01-29 00:08:32 -08004It requires at least Mac OS X 10.5, Xcode 3, and the 10.4u SDK for
Ned Deilye59e4c52011-01-29 18:56:28 +0000532-bit builds. 64-bit or four-way universal builds require at least
6OS X 10.5 and the 10.5 SDK.
Thomas Wouters477c8d52006-05-27 19:21:47 +00007
Ned Deilye59e4c52011-01-29 18:56:28 +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 Deily9978a932014-04-09 16:15:20 -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.
Thomas Wouters477c8d52006-05-27 19:21:47 +000014
Ned Deily4a96a372013-01-29 00:08:32 -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 Deily9978a932014-04-09 16:15:20 -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 Deily4a96a372013-01-29 00:08:32 -080028
Thomas Wouters477c8d52006-05-27 19:21:47 +000029Usage: see USAGE variable in the script.
30"""
Ned Deily4a96a372013-01-29 00:08:32 -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 )
Thomas Wouters477c8d52006-05-27 19:21:47 +000044
Thomas Wouters89f507f2006-12-13 04:49:30 +000045INCLUDE_TIMESTAMP = 1
46VERBOSE = 1
Thomas Wouters477c8d52006-05-27 19:21:47 +000047
48from plistlib import Plist
49
Thomas Wouters477c8d52006-05-27 19:21:47 +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 """
Thomas Wouters89f507f2006-12-13 04:49:30 +000059 Return the string value in a form that can safely be inserted into
Thomas Wouters477c8d52006-05-27 19:21:47 +000060 a shell command.
61 """
62 return "'%s'"%(value.replace("'", "'\"'\"'"))
63
64def grepValue(fn, variable):
Ned Deily5d3febf2014-12-13 00:17:46 -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 """
Thomas Wouters477c8d52006-05-27 19:21:47 +000070 variable = variable + '='
71 for ln in open(fn, 'r'):
72 if ln.startswith(variable):
73 value = ln[len(variable):].strip()
Ned Deily5d3febf2014-12-13 00:17:46 -080074 return value.strip("\"'")
Ned Deily4a96a372013-01-29 00:08:32 -080075 raise RuntimeError("Cannot find variable %s" % variable[:-1])
76
77_cache_getVersion = None
Thomas Wouters477c8d52006-05-27 19:21:47 +000078
79def getVersion():
Ned Deily4a96a372013-01-29 00:08:32 -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
Thomas Wouters477c8d52006-05-27 19:21:47 +000085
Ned Deily4a96a372013-01-29 00:08:32 -080086def getVersionMajorMinor():
87 return tuple([int(n) for n in getVersion().split('.', 2)])
88
89_cache_getFullVersion = None
90
Thomas Wouters477c8d52006-05-27 19:21:47 +000091def getFullVersion():
Ned Deily4a96a372013-01-29 00:08:32 -080092 global _cache_getFullVersion
93 if _cache_getFullVersion is not None:
94 return _cache_getFullVersion
Thomas Wouters477c8d52006-05-27 19:21:47 +000095 fn = os.path.join(SRCDIR, 'Include', 'patchlevel.h')
96 for ln in open(fn):
97 if 'PY_VERSION' in ln:
Ned Deily4a96a372013-01-29 00:08:32 -080098 _cache_getFullVersion = ln.split()[-1][1:-1]
99 return _cache_getFullVersion
100 raise RuntimeError("Cannot find full version??")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000101
Ned Deily5d3febf2014-12-13 00:17:46 -0800102FW_PREFIX = ["Library", "Frameworks", "Python.framework"]
103FW_VERSION_PREFIX = "--undefined--" # initialized in parseOptions
104
Thomas Wouters89f507f2006-12-13 04:49:30 +0000105# The directory we'll use to create the build (will be erased and recreated)
106WORKDIR = "/tmp/_py"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000107
Thomas Wouters89f507f2006-12-13 04:49:30 +0000108# The directory we'll use to store third-party sources. Set this to something
Thomas Wouters477c8d52006-05-27 19:21:47 +0000109# else if you don't want to re-fetch required libraries every time.
Thomas Wouters89f507f2006-12-13 04:49:30 +0000110DEPSRC = os.path.join(WORKDIR, 'third-party')
111DEPSRC = os.path.expanduser('~/Universal/other-sources')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000112
113# Location of the preferred SDK
Benjamin Petersond9b7d482010-03-19 21:42:45 +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 Melotti7c4a7e62013-08-26 01:32:56 +0300118### issue is resolved.
Benjamin Petersond9b7d482010-03-19 21:42:45 +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
Thomas Wouters89f507f2006-12-13 04:49:30 +0000128SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000129
Ronald Oussoren1943f862009-03-30 19:39:14 +0000130universal_opts_map = { '32-bit': ('i386', 'ppc',),
131 '64-bit': ('x86_64', 'ppc64',),
Benjamin Petersond9b7d482010-03-19 21:42:45 +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 Oussoren1943f862009-03-30 19:39:14 +0000141
142UNIVERSALOPTS = tuple(universal_opts_map.keys())
143
144UNIVERSALARCHS = '32-bit'
145
146ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000147
Ezio Melotti42da6632011-03-15 05:18:48 +0200148# Source directory (assume we're in Mac/BuildScript)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000149SRCDIR = os.path.dirname(
Thomas Wouters477c8d52006-05-27 19:21:47 +0000150 os.path.dirname(
151 os.path.dirname(
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000152 os.path.abspath(__file__
153 ))))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000154
Ronald Oussoren1943f862009-03-30 19:39:14 +0000155# $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level
156DEPTARGET = '10.3'
157
Ned Deily04cdfa12014-06-25 13:36:14 -0700158def getDeptargetTuple():
159 return tuple([int(n) for n in DEPTARGET.split('.')[0:2]])
160
161def getTargetCompilers():
162 target_cc_map = {
Ned Deily4a96a372013-01-29 00:08:32 -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 Deily04cdfa12014-06-25 13:36:14 -0700167 }
168 return target_cc_map.get(DEPTARGET, ('clang', 'clang++') )
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000169
Ned Deily04cdfa12014-06-25 13:36:14 -0700170CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000171
Ned Deily5d3febf2014-12-13 00:17:46 -0800172PYTHON_3 = getVersionMajorMinor() >= (3, 0)
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000173
Thomas Wouters89f507f2006-12-13 04:49:30 +0000174USAGE = textwrap.dedent("""\
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Oussoren1943f862009-03-30 19:39:14 +0000184 --dep-target=10.n OS X deployment target (default: %(DEPTARGET)r)
185 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000186""")% globals()
187
Ned Deily4a96a372013-01-29 00:08:32 -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 = {}
Thomas Wouters477c8d52006-05-27 19:21:47 +0000195
Ned Deily5d3febf2014-12-13 00:17:46 -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
Thomas Wouters477c8d52006-05-27 19:21:47 +0000200# Instructions for building libraries that are necessary for building a
201# batteries included python.
Ronald Oussoren1943f862009-03-30 19:39:14 +0000202# [The recipes are defined here for convenience but instantiated later after
203# command line options have been processed.]
204def library_recipes():
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000205 result = []
Thomas Wouters477c8d52006-05-27 19:21:47 +0000206
Ned Deily04cdfa12014-06-25 13:36:14 -0700207 LT_10_5 = bool(getDeptargetTuple() < (10, 5))
Ned Deily4a96a372013-01-29 00:08:32 -0800208
Ned Deily59884982016-02-25 01:01:48 +1100209 if not (10, 5) < getDeptargetTuple() < (10, 10):
Ned Deily5d3febf2014-12-13 00:17:46 -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 Deily59884982016-02-25 01:01:48 +1100229 # For builds with 10.6 through 10.9 SDKs,
230 # continue to use the deprecated but
Ned Deily5d3febf2014-12-13 00:17:46 -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 Deily59884982016-02-25 01:01:48 +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 Deily5d3febf2014-12-13 00:17:46 -0800242
243 result.extend([
244 dict(
Ned Deilyf4d97ce2016-03-08 01:09:08 -0500245 name="OpenSSL 1.0.2g",
246 url="https://www.openssl.org/source/openssl-1.0.2g.tar.gz",
247 checksum='f3c710c045cdee5fd114feb69feba7aa',
Ned Deily5d3febf2014-12-13 00:17:46 -0800248 patches=[
249 "openssl_sdk_makedepend.patch",
250 ],
251 buildrecipe=build_universal_openssl,
252 configure=None,
253 install=None,
254 ),
255 ])
256
Ned Deilyaa6a2122013-11-23 03:30:11 -0800257# Disable for now
Ned Deilyed730102014-11-14 18:55:05 -0800258 if False: # if getDeptargetTuple() > (10, 5):
Ned Deily5b3582c2013-10-25 00:41:46 -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 Deily94764b22013-10-27 19:49:29 -0700280 patches=[
281 "issue19373_tk_8_5_15_source.patch",
282 ],
Ned Deily5b3582c2013-10-25 00:41:46 -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 Deilyed730102014-11-14 18:55:05 -0800299 if PYTHON_3:
Ned Deily4a96a372013-01-29 00:08:32 -0800300 result.extend([
301 dict(
Ned Deily9fa4ced2013-11-22 22:54:02 -0800302 name="XZ 5.0.5",
303 url="http://tukaani.org/xz/xz-5.0.5.tar.gz",
304 checksum='19d924e066b6fff0bc9d1981b4e53196',
Ned Deily4a96a372013-01-29 00:08:32 -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 Deilybb1c0792015-07-28 23:22:23 -0700347 name="SQLite 3.8.11",
348 url="https://www.sqlite.org/2015/sqlite-autoconf-3081100.tar.gz",
349 checksum='77b451925121028befbddbf45ea2bc49',
Ned Deily4a96a372013-01-29 00:08:32 -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 Deily04cdfa12014-06-25 13:36:14 -0700366 if getDeptargetTuple() < (10, 5):
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000367 result.extend([
368 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000369 name="Bzip2 1.0.6",
370 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
371 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000372 configure=None,
Ned Deily4a96a372013-01-29 00:08:32 -0800373 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
374 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000375 shellQuote(os.path.join(WORKDIR, 'libraries')),
376 ' -arch '.join(ARCHLIST),
377 SDKPATH,
378 ),
379 ),
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 Deily4a96a372013-01-29 00:08:32 -0800385 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
386 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +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 Deily4f7ff782011-01-15 05:29:12 +0000394 name="GNU Readline 6.1.2",
395 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
396 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000397 patchlevel='0',
398 patches=[
399 # The readline maintainers don't do actual micro releases, but
400 # just ship a set of patches.
Ned Deily4a96a372013-01-29 00:08:32 -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'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000405 ]
406 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000407 ])
408
Ned Deily4f7ff782011-01-15 05:29:12 +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 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000422
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000423 return result
424
Thomas Wouters477c8d52006-05-27 19:21:47 +0000425
Thomas Wouters477c8d52006-05-27 19:21:47 +0000426# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000427def pkg_recipes():
428 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
429 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",
440 selected='selected',
441 ),
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).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000450
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000451 It also installs a number of examples and demos.
452 """,
453 required=False,
454 selected='selected',
455 ),
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,
466 selected='selected',
467 ),
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 Deily4a96a372013-01-29 00:08:32 -0800475 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000476 """,
477 postflight="scripts/postflight.documentation",
478 required=False,
479 selected='selected',
480 ),
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.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000488
Benjamin Petersond9b7d482010-03-19 21:42:45 +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 Deilyed730102014-11-14 18:55:05 -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',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000511 ),
512 ]
513
Ned Deily04cdfa12014-06-25 13:36:14 -0700514 if getDeptargetTuple() < (10, 4) and not PYTHON_3:
Benjamin Petersond9b7d482010-03-19 21:42:45 +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,
528 selected=unselected_for_python3,
529 )
530 )
Ned Deily41ab6c32013-11-22 22:25:43 -0800531
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000532 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000533
Thomas Wouters477c8d52006-05-27 19:21:47 +0000534def 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 Deily4a96a372013-01-29 00:08:32 -0800547 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000548
549def runCommand(commandline):
550 """
Ezio Melotti13925002011-03-16 11:05:33 +0200551 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000552 unless the command fails.
553 """
554 fd = os.popen(commandline, 'r')
555 data = fd.read()
556 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000557 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000558 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800559 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Peterson2a691a82008-03-31 01:51:45 +0000568 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000569 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800570 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000571
572 return data
573
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000574def getTclTkVersion(configfile, versionline):
575 """
576 search Tcl or Tk configuration file for version line
577 """
578 try:
579 f = open(configfile, "r")
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300580 except OSError:
Ronald Oussorenc45c3d92010-04-18 15:24:17 +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
Thomas Wouters477c8d52006-05-27 19:21:47 +0000591def checkEnvironment():
592 """
593 Check that we're running on a supported system.
594 """
595
Ned Deilye59e4c52011-01-29 18:56:28 +0000596 if sys.version_info[0:2] < (2, 4):
597 fatal("This script must be run with Python 2.4 or later")
598
Thomas Wouters477c8d52006-05-27 19:21:47 +0000599 if platform.system() != 'Darwin':
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000600 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000601
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000602 if int(platform.release().split('.')[0]) < 8:
603 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Oussorenc45c3d92010-04-18 15:24:17 +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 Deily4a96a372013-01-29 00:08:32 -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 Oussorenc45c3d92010-04-18 15:24:17 +0000617
Ned Deily4a96a372013-01-29 00:08:32 -0800618 frameworks = {}
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000619 for framework in ['Tcl', 'Tk']:
Ned Deily4a96a372013-01-29 00:08:32 -0800620 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000621 sysfw = os.path.join(SDKPATH, 'System', fwpth)
Ned Deily4a96a372013-01-29 00:08:32 -0800622 libfw = os.path.join(SDKPATH, fwpth)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000623 usrfw = os.path.join(os.getenv('HOME'), fwpth)
Ned Deily4a96a372013-01-29 00:08:32 -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))
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000629 if os.readlink(libfw) != os.readlink(sysfw):
Ronald Oussorenc45c3d92010-04-18 15:24:17 +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 Deily4a96a372013-01-29 00:08:32 -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 Oussorenc45c3d92010-04-18 15:24:17 +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 Deily4a96a372013-01-29 00:08:32 -0800652 print("INFO: deleting environment variable %s=%s" % (
653 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000654 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000655
Ned Deily4a96a372013-01-29 00:08:32 -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 Deily7e60f512014-04-07 12:10:21 -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 Deily1ff32a92014-09-05 15:57:05 -0700669 runCommand('sphinx-build --version')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000670
Thomas Wouters89f507f2006-12-13 04:49:30 +0000671def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000672 """
673 Parse arguments and update global settings.
674 """
Ronald Oussoren1943f862009-03-30 19:39:14 +0000675 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
Ned Deily4a96a372013-01-29 00:08:32 -0800676 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily5d3febf2014-12-13 00:17:46 -0800677 global FW_VERSION_PREFIX
Thomas Wouters477c8d52006-05-27 19:21:47 +0000678
679 if args is None:
680 args = sys.argv[1:]
681
682 try:
683 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000684 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
685 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily4a96a372013-01-29 00:08:32 -0800686 except getopt.GetoptError:
687 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000688 sys.exit(1)
689
690 if args:
Ned Deily4a96a372013-01-29 00:08:32 -0800691 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000692 sys.exit(1)
693
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000694 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000695 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000696 if k in ('-h', '-?', '--help'):
Ned Deily4a96a372013-01-29 00:08:32 -0800697 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Oussoren1943f862009-03-30 19:39:14 +0000712 elif k in ('--dep-target', ):
713 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000714 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000715
716 elif k in ('--universal-archs', ):
717 if v in UNIVERSALOPTS:
718 UNIVERSALARCHS = v
719 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000720 if deptarget is None:
721 # Select alternate default deployment
722 # target
723 DEPTARGET = default_target_map.get(v, '10.3')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000724 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800725 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000726
Thomas Wouters477c8d52006-05-27 19:21:47 +0000727 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800728 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily04cdfa12014-06-25 13:36:14 -0700735 CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000736
Ned Deily5d3febf2014-12-13 00:17:46 -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 Deily4a96a372013-01-29 00:08:32 -0800748 print("")
Ned Deily5d3febf2014-12-13 00:17:46 -0800749 print(" -- Building a Python %s framework at patch level %s"
750 % (getVersion(), getFullVersion()))
751 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily5b3582c2013-10-25 00:41:46 -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.
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily5b3582c2013-10-25 00:41:46 -0700770 if ((retval.startswith('tcl') or retval.startswith('tk'))
771 and retval.endswith('-src')):
772 retval = retval[:-4]
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily4a96a372013-01-29 00:08:32 -0800799 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000800
801 return os.path.join(builddir, retval)
802
803 finally:
804 os.chdir(curdir)
805
Thomas Wouters477c8d52006-05-27 19:21:47 +0000806def downloadURL(url, fname):
807 """
808 Download the contents of the url into the file.
809 """
Ned Deily4a96a372013-01-29 00:08:32 -0800810 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +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)
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300822 except OSError:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000823 pass
824
Ned Deily4a96a372013-01-29 00:08:32 -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 Deily5d3febf2014-12-13 00:17:46 -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
852 # 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
857 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 ]
881 if no_asm:
882 configure_opts.append("no-asm")
883 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
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily5d3febf2014-12-13 00:17:46 -0800978 THIRD_PARTY_LIBS.append(name)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000979 url = recipe['url']
980 configure = recipe.get('configure', './configure')
Ned Deily5d3febf2014-12-13 00:17:46 -0800981 buildrecipe = recipe.get('buildrecipe', None)
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily4a96a372013-01-29 00:08:32 -0800991 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
992 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +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)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000999
Ned Deily4a96a372013-01-29 00:08:32 -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)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001008 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
1009 shellQuote(fn),))
1010
Ned Deily4a96a372013-01-29 00:08:32 -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 Deily94764b22013-10-27 19:49:29 -07001025 if 'buildDir' in recipe:
1026 os.chdir(recipe['buildDir'])
1027
Thomas Wouters0e3f5912006-08-11 14:57:12 +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 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001035
Thomas Wouters0e3f5912006-08-11 14:57:12 +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)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001043
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001044 if recipe.get('useLDFlags', 1):
1045 configure_args.extend([
Ned Deily4a96a372013-01-29 00:08:32 -08001046 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
1047 "-I%s/usr/local/include"%(
1048 recipe.get('extra_cflags', ''),
1049 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001050 ' -arch '.join(archList),
1051 shellQuote(SDKPATH)[1:-1],
1052 shellQuote(basedir)[1:-1],),
Ned Deily0134a352014-04-09 16:16:08 -07001053 "LDFLAGS=-mmacosx-version-min=%s -isysroot %s -L%s/usr/local/lib -arch %s"%(
Ned Deily4a96a372013-01-29 00:08:32 -08001054 DEPTARGET,
Thomas Wouters477c8d52006-05-27 19:21:47 +00001055 shellQuote(SDKPATH)[1:-1],
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001056 shellQuote(basedir)[1:-1],
1057 ' -arch '.join(archList)),
1058 ])
1059 else:
1060 configure_args.extend([
Ned Deily4a96a372013-01-29 00:08:32 -08001061 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
1062 "-I%s/usr/local/include"%(
1063 recipe.get('extra_cflags', ''),
1064 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001065 ' -arch '.join(archList),
1066 shellQuote(SDKPATH)[1:-1],
1067 shellQuote(basedir)[1:-1],),
1068 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001069
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001070 if 'configure_post' in recipe:
Ned Deily4a96a372013-01-29 00:08:32 -08001071 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001072
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001073 configure_args.insert(0, configure)
1074 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001075
Ned Deily4a96a372013-01-29 00:08:32 -08001076 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001077 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001078
Ned Deily5d3febf2014-12-13 00:17:46 -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')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001086
Ned Deily4a96a372013-01-29 00:08:32 -08001087 print("Done %s"%(name,))
1088 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001089
1090 os.chdir(curdir)
1091
1092def buildLibraries():
1093 """
1094 Build our dependencies into $WORKDIR/libraries/usr/local
1095 """
Ned Deily4a96a372013-01-29 00:08:32 -08001096 print("")
1097 print("Building required libraries")
1098 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Oussoren1943f862009-03-30 19:39:14 +00001104 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001105 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001106
1107
1108
1109def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001110 # This stores the documentation as Resources/English.lproj/Documentation
Thomas Wouters477c8d52006-05-27 19:21:47 +00001111 # inside the framwork. pydoc and IDLE will pick it up there.
Ned Deily4a96a372013-01-29 00:08:32 -08001112 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001113 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001114 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001115 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001116 curDir = os.getcwd()
1117 os.chdir(buildDir)
Ned Deily1ff32a92014-09-05 15:57:05 -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 Oussoren207b4c22009-03-30 17:20:30 +00001122 os.chdir(curDir)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001123 if not os.path.exists(docdir):
1124 os.mkdir(docdir)
Ronald Oussorenf84d7e92009-05-19 11:27:25 +00001125 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001126
1127
1128def buildPython():
Ned Deily4a96a372013-01-29 00:08:32 -08001129 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily4f7ff782011-01-15 05:29:12 +00001138 os.makedirs(buildDir)
1139 os.makedirs(rootDir)
1140 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Oussorenac4b39f2009-03-30 20:05:35 +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 Deily4a96a372013-01-29 00:08:32 -08001157 print("Running configure...")
Ronald Oussoren1943f862009-03-30 19:39:14 +00001158 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001159 "--with-universal-archs=%s "
1160 "%s "
Ned Deily41ab6c32013-11-22 22:25:43 -08001161 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +00001162 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deily4b7a0232013-10-25 00:46:02 -07001163 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001164 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
1165 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001166 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deilyed730102014-11-14 18:55:05 -08001167 (' ', '--without-ensurepip ')[PYTHON_3],
Ronald Oussoren1943f862009-03-30 19:39:14 +00001168 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +00001169 shellQuote(WORKDIR)[1:-1]))
1170
Ned Deily729148b2014-05-22 15:28:06 -07001171 print("Running make touch")
1172 runCommand("make touch")
1173
Ned Deily4a96a372013-01-29 00:08:32 -08001174 print("Running make")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001175 runCommand("make")
1176
Ned Deily4a96a372013-01-29 00:08:32 -08001177 print("Running make install")
Ronald Oussorenf84d7e92009-05-19 11:27:25 +00001178 runCommand("make install DESTDIR=%s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001179 shellQuote(rootDir)))
1180
Ned Deily4a96a372013-01-29 00:08:32 -08001181 print("Running make frameworkinstallextras")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001182 runCommand("make frameworkinstallextras DESTDIR=%s"%(
1183 shellQuote(rootDir)))
1184
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001185 del os.environ['DYLD_LIBRARY_PATH']
Ned Deily4a96a372013-01-29 00:08:32 -08001186 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily050fcd52013-10-26 03:16:44 -07001197 path_to_lib = os.path.join(rootDir, 'Library', 'Frameworks',
1198 'Python.framework', 'Versions',
1199 version, 'lib', 'python%s'%(version,))
1200
Ned Deily4a96a372013-01-29 00:08:32 -08001201 print("Fix file modes")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001202 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Thomas Wouters89f507f2006-12-13 04:49:30 +00001203 gid = grp.getgrnam('admin').gr_gid
1204
Ned Deily4a96a372013-01-29 00:08:32 -08001205 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00001206 for dirpath, dirnames, filenames in os.walk(frmDir):
1207 for dn in dirnames:
Ned Deily4a96a372013-01-29 00:08:32 -08001208 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001209 os.chown(os.path.join(dirpath, dn), -1, gid)
1210
Thomas Wouters477c8d52006-05-27 19:21:47 +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)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001218 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1219 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001220
Ned Deily4a96a372013-01-29 00:08:32 -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 Deilye59e4c52011-01-29 18:56:28 +00001234 if PYTHON_3:
1235 LDVERSION=None
1236 VERSION=None
1237 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001238
Ned Deilye59e4c52011-01-29 18:56:28 +00001239 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001240 for ln in fp:
1241 if ln.startswith('VERSION='):
1242 VERSION=ln.split()[1]
1243 if ln.startswith('ABIFLAGS='):
1244 ABIFLAGS=ln.split()[1]
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001245 if ln.startswith('LDVERSION='):
1246 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +00001247 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001248
Ned Deilye59e4c52011-01-29 18:56:28 +00001249 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1250 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1251 config_suffix = '-' + LDVERSION
1252 else:
1253 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001254
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily4a96a372013-01-29 00:08:32 -08001257 # the end-users system. Also remove the directories from _sysconfigdata.py
1258 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001259
Ned Deilya4f6b002013-10-25 00:47:38 -07001260 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1261 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1262
Ned Deilya4f6b002013-10-25 00:47:38 -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 Deily4a96a372013-01-29 00:08:32 -08001293 fp = open(path, 'r')
1294 data = fp.read()
1295 fp.close()
Ned Deilya4f6b002013-10-25 00:47:38 -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 Deily4a96a372013-01-29 00:08:32 -08001305
Ned Deily4a96a372013-01-29 00:08:32 -08001306 fp = open(path, 'w')
Ned Deilya4f6b002013-10-25 00:47:38 -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 Deily4a96a372013-01-29 00:08:32 -08001312 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +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
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001328 if PYTHON_3:
Ezio Melotti7c4a7e62013-08-26 01:32:56 +03001329 # Remove the 'Current' link, that way we don't accidentally mess
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001330 # with an already installed version of python 2
1331 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1332 'Python.framework', 'Versions', 'Current'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001333
1334def patchFile(inPath, outPath):
1335 data = fileContents(inPath)
1336 data = data.replace('$FULL_VERSION', getFullVersion())
1337 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001338 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001339 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001340 data = data.replace('$INSTALL_SIZE', installSize())
Ned Deily5d3febf2014-12-13 00:17:46 -08001341 data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001342
1343 # This one is not handy as a template variable
1344 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily4a96a372013-01-29 00:08:32 -08001345 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001346 fp.write(data)
1347 fp.close()
1348
1349def patchScript(inPath, outPath):
Ned Deilyed730102014-11-14 18:55:05 -08001350 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001351 data = fileContents(inPath)
Ned Deilyed730102014-11-14 18:55:05 -08001352 data = data.replace('@PYMAJOR@', str(major))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001353 data = data.replace('@PYVER@', getVersion())
Ned Deily4a96a372013-01-29 00:08:32 -08001354 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001355 fp.write(data)
1356 fp.close()
Ned Deily4a96a372013-01-29 00:08:32 -08001357 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001358
1359
1360
1361def packageFromRecipe(targetDir, recipe):
1362 curdir = os.getcwd()
1363 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +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
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001366 # common.
1367 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily4a96a372013-01-29 00:08:32 -08001374 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily4a96a372013-01-29 00:08:32 -08001419 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001420 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001421 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1422 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1423 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Oussoren1943f862009-03-30 19:39:14 +00001444 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily4a96a372013-01-29 00:08:32 -08001456 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001457
1458 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001459 CFBundleGetInfoString="Python %s"%(vers,),
1460 CFBundleIdentifier='org.python.Python',
1461 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001462 CFBundleShortVersionString=vers,
1463 IFMajorVersion=major,
1464 IFMinorVersion=minor,
1465 IFPkgFlagComponentDirectory="Contents/Packages",
1466 IFPkgFlagPackageList=[
1467 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001468 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001469 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001470 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001471 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001472 ],
1473 IFPkgFormatVersion=0.10000000149011612,
1474 IFPkgFlagBackgroundScaling="proportional",
1475 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001476 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Oussoren1943f862009-03-30 19:39:14 +00001495 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001496 pkgcontents = os.path.join(pkgroot, 'Packages')
1497 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001498 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Oussoren1943f862009-03-30 19:39:14 +00001512 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +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
Thomas Wouters477c8d52006-05-27 19:21:47 +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 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001537 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +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,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001544 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001545 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001546 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001547 imagepath = imagepath + '.dmg'
1548
1549 os.mkdir(outdir)
Ronald Oussoren1943f862009-03-30 19:39:14 +00001550 volname='Python %s'%(getFullVersion())
1551 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1552 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001553 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +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 Deily4a96a372013-01-29 00:08:32 -08001565 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +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")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001576
1577 return imagepath
1578
1579
1580def setIcon(filePath, icnsPath):
1581 """
1582 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001583 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001584
Ronald Oussoren70050672010-04-30 15:00:26 +00001585 dirPath = os.path.normpath(os.path.dirname(__file__))
1586 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +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 Oussoren70050672010-04-30 15:00:26 +00001590 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1591 if not os.path.exists(appPath):
1592 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001593 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1594 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001595
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001596 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1597 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001598
1599def main():
1600 # First parse options and check if we can perform our work
1601 parseOptions()
1602 checkEnvironment()
1603
Ronald Oussoren1943f862009-03-30 19:39:14 +00001604 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001605 os.environ['CC'] = CC
Ned Deily4a96a372013-01-29 00:08:32 -08001606 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001607
1608 if os.path.exists(WORKDIR):
1609 shutil.rmtree(WORKDIR)
1610 os.mkdir(WORKDIR)
1611
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001612 os.environ['LC_ALL'] = 'C'
1613
Thomas Wouters477c8d52006-05-27 19:21:47 +00001614 # Then build third-party libraries such as sleepycat DB4.
1615 buildLibraries()
1616
1617 # Now build python itself
1618 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +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']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001627 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001628
1629
1630 # Prepare the applications folder
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001631 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001632 getVersion(),))
Ned Deily5d3febf2014-12-13 00:17:46 -08001633 fn = os.path.join(folder, "License.rtf")
1634 patchFile("resources/License.rtf", fn)
1635 fn = os.path.join(folder, "ReadMe.rtf")
1636 patchFile("resources/ReadMe.rtf", fn)
1637 fn = os.path.join(folder, "Update Shell Profile.command")
1638 patchScript("scripts/postflight.patch-profile", fn)
Ned Deily4a96a372013-01-29 00:08:32 -08001639 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +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 Deily5d3febf2014-12-13 00:17:46 -08001646 patchFile('resources/ReadMe.rtf',
1647 os.path.join(WORKDIR, 'installer', 'ReadMe.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001648
1649 # Ditto for the license file.
Ned Deily5d3febf2014-12-13 00:17:46 -08001650 patchFile('resources/License.rtf',
1651 os.path.join(WORKDIR, 'installer', 'License.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001652
1653 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily4a96a372013-01-29 00:08:32 -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)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001657 fp.close()
1658
Thomas Wouters477c8d52006-05-27 19:21:47 +00001659 # And copy it to a DMG
1660 buildDMG()
1661
Thomas Wouters477c8d52006-05-27 19:21:47 +00001662if __name__ == "__main__":
1663 main()