blob: ef93a6edfed01b3fe157727a6261eb0fac9bae88 [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 Deily92b4b5a2016-06-04 17:21:21 -0700245 name="OpenSSL 1.0.2h",
246 url="https://www.openssl.org/source/openssl-1.0.2h.tar.gz",
247 checksum='9392e65072ce4b614c1392eefc1f23d0',
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 Deilyb3b07672016-09-05 17:31:14 -0700302 name="XZ 5.2.2",
303 url="http://tukaani.org/xz/xz-5.2.2.tar.gz",
304 checksum='7cf6a8544a7dae8e8106fdf7addfa28c',
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 Deilyb3b07672016-09-05 17:31:14 -0700347 name="SQLite 3.14.1",
348 url="https://www.sqlite.org/2016/sqlite-autoconf-3140100.tar.gz",
349 checksum='3634a90a3f49541462bcaed3474b2684',
Ned Deily4a96a372013-01-29 00:08:32 -0800350 extra_cflags=('-Os '
Ned Deilyb3b07672016-09-05 17:31:14 -0700351 '-DSQLITE_ENABLE_FTS5 '
Ned Deily4a96a372013-01-29 00:08:32 -0800352 '-DSQLITE_ENABLE_FTS4 '
353 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
354 '-DSQLITE_ENABLE_RTREE '
355 '-DSQLITE_TCL=0 '
356 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
357 configure_pre=[
358 '--enable-threadsafe',
359 '--enable-shared=no',
360 '--enable-static=yes',
361 '--disable-readline',
362 '--disable-dependency-tracking',
363 ]
364 ),
365 ])
366
Ned Deily04cdfa12014-06-25 13:36:14 -0700367 if getDeptargetTuple() < (10, 5):
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000368 result.extend([
369 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000370 name="Bzip2 1.0.6",
371 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
372 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000373 configure=None,
Ned Deily4a96a372013-01-29 00:08:32 -0800374 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
375 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000376 shellQuote(os.path.join(WORKDIR, 'libraries')),
377 ' -arch '.join(ARCHLIST),
378 SDKPATH,
379 ),
380 ),
381 dict(
382 name="ZLib 1.2.3",
383 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
384 checksum='debc62758716a169df9f62e6ab2bc634',
385 configure=None,
Ned Deily4a96a372013-01-29 00:08:32 -0800386 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
387 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000388 shellQuote(os.path.join(WORKDIR, 'libraries')),
389 ' -arch '.join(ARCHLIST),
390 SDKPATH,
391 ),
392 ),
393 dict(
394 # Note that GNU readline is GPL'd software
Ned Deily4f7ff782011-01-15 05:29:12 +0000395 name="GNU Readline 6.1.2",
396 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
397 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000398 patchlevel='0',
399 patches=[
400 # The readline maintainers don't do actual micro releases, but
401 # just ship a set of patches.
Ned Deily4a96a372013-01-29 00:08:32 -0800402 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
403 'c642f2e84d820884b0bf9fd176bc6c3f'),
404 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
405 '1a76781a1ea734e831588285db7ec9b1'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000406 ]
407 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000408 ])
409
Ned Deily4f7ff782011-01-15 05:29:12 +0000410 if not PYTHON_3:
411 result.extend([
412 dict(
413 name="Sleepycat DB 4.7.25",
414 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
415 checksum='ec2b87e833779681a0c3a814aa71359e',
416 buildDir="build_unix",
417 configure="../dist/configure",
418 configure_pre=[
419 '--includedir=/usr/local/include/db4',
420 ]
421 ),
422 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000423
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000424 return result
425
Thomas Wouters477c8d52006-05-27 19:21:47 +0000426
Thomas Wouters477c8d52006-05-27 19:21:47 +0000427# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000428def pkg_recipes():
429 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
430 result = [
431 dict(
432 name="PythonFramework",
433 long_name="Python Framework",
434 source="/Library/Frameworks/Python.framework",
435 readme="""\
436 This package installs Python.framework, that is the python
437 interpreter and the standard library. This also includes Python
438 wrappers for lots of Mac OS X API's.
439 """,
440 postflight="scripts/postflight.framework",
441 selected='selected',
442 ),
443 dict(
444 name="PythonApplications",
445 long_name="GUI Applications",
446 source="/Applications/Python %(VER)s",
447 readme="""\
448 This package installs IDLE (an interactive Python IDE),
449 Python Launcher and Build Applet (create application bundles
450 from python scripts).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000451
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000452 It also installs a number of examples and demos.
453 """,
454 required=False,
455 selected='selected',
456 ),
457 dict(
458 name="PythonUnixTools",
459 long_name="UNIX command-line tools",
460 source="/usr/local/bin",
461 readme="""\
462 This package installs the unix tools in /usr/local/bin for
463 compatibility with older releases of Python. This package
464 is not necessary to use Python.
465 """,
466 required=False,
467 selected='selected',
468 ),
469 dict(
470 name="PythonDocumentation",
471 long_name="Python Documentation",
472 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
473 source="/pydocs",
474 readme="""\
475 This package installs the python documentation at a location
Ned Deily4a96a372013-01-29 00:08:32 -0800476 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000477 """,
478 postflight="scripts/postflight.documentation",
479 required=False,
480 selected='selected',
481 ),
482 dict(
483 name="PythonProfileChanges",
484 long_name="Shell profile updater",
485 readme="""\
486 This packages updates your shell profile to make sure that
487 the Python tools are found by your shell in preference of
488 the system provided Python tools.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000489
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000490 If you don't install this package you'll have to add
491 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
492 to your PATH by hand.
493 """,
494 postflight="scripts/postflight.patch-profile",
495 topdir="/Library/Frameworks/Python.framework",
496 source="/empty-dir",
497 required=False,
Ned Deilyed730102014-11-14 18:55:05 -0800498 selected='selected',
499 ),
500 dict(
501 name="PythonInstallPip",
502 long_name="Install or upgrade pip",
503 readme="""\
504 This package installs (or upgrades from an earlier version)
505 pip, a tool for installing and managing Python packages.
506 """,
507 postflight="scripts/postflight.ensurepip",
508 topdir="/Library/Frameworks/Python.framework",
509 source="/empty-dir",
510 required=False,
511 selected='selected',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000512 ),
513 ]
514
Ned Deily04cdfa12014-06-25 13:36:14 -0700515 if getDeptargetTuple() < (10, 4) and not PYTHON_3:
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000516 result.append(
517 dict(
518 name="PythonSystemFixes",
519 long_name="Fix system Python",
520 readme="""\
521 This package updates the system python installation on
522 Mac OS X 10.3 to ensure that you can build new python extensions
523 using that copy of python after installing this version.
524 """,
525 postflight="../Tools/fixapplepython23.py",
526 topdir="/Library/Frameworks/Python.framework",
527 source="/empty-dir",
528 required=False,
529 selected=unselected_for_python3,
530 )
531 )
Ned Deily41ab6c32013-11-22 22:25:43 -0800532
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000533 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000534
Thomas Wouters477c8d52006-05-27 19:21:47 +0000535def fatal(msg):
536 """
537 A fatal error, bail out.
538 """
539 sys.stderr.write('FATAL: ')
540 sys.stderr.write(msg)
541 sys.stderr.write('\n')
542 sys.exit(1)
543
544def fileContents(fn):
545 """
546 Return the contents of the named file
547 """
Ned Deily4a96a372013-01-29 00:08:32 -0800548 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000549
550def runCommand(commandline):
551 """
Ezio Melotti13925002011-03-16 11:05:33 +0200552 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000553 unless the command fails.
554 """
555 fd = os.popen(commandline, 'r')
556 data = fd.read()
557 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000558 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000559 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800560 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000561
562 if VERBOSE:
563 sys.stdout.write(data); sys.stdout.flush()
564
565def captureCommand(commandline):
566 fd = os.popen(commandline, 'r')
567 data = fd.read()
568 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000569 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000570 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800571 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000572
573 return data
574
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000575def getTclTkVersion(configfile, versionline):
576 """
577 search Tcl or Tk configuration file for version line
578 """
579 try:
580 f = open(configfile, "r")
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300581 except OSError:
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000582 fatal("Framework configuration file not found: %s" % configfile)
583
584 for l in f:
585 if l.startswith(versionline):
586 f.close()
587 return l
588
589 fatal("Version variable %s not found in framework configuration file: %s"
590 % (versionline, configfile))
591
Thomas Wouters477c8d52006-05-27 19:21:47 +0000592def checkEnvironment():
593 """
594 Check that we're running on a supported system.
595 """
596
Ned Deilye59e4c52011-01-29 18:56:28 +0000597 if sys.version_info[0:2] < (2, 4):
598 fatal("This script must be run with Python 2.4 or later")
599
Thomas Wouters477c8d52006-05-27 19:21:47 +0000600 if platform.system() != 'Darwin':
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000601 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000602
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000603 if int(platform.release().split('.')[0]) < 8:
604 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000605
606 if not os.path.exists(SDKPATH):
607 fatal("Please install the latest version of Xcode and the %s SDK"%(
608 os.path.basename(SDKPATH[:-4])))
609
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000610 # Because we only support dynamic load of only one major/minor version of
611 # Tcl/Tk, ensure:
612 # 1. there are no user-installed frameworks of Tcl/Tk with version
Ned Deily4a96a372013-01-29 00:08:32 -0800613 # higher than the Apple-supplied system version in
614 # SDKROOT/System/Library/Frameworks
615 # 2. there is a user-installed framework (usually ActiveTcl) in (or linked
616 # in) SDKROOT/Library/Frameworks with the same version as the system
617 # version. This allows users to choose to install a newer patch level.
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000618
Ned Deily4a96a372013-01-29 00:08:32 -0800619 frameworks = {}
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000620 for framework in ['Tcl', 'Tk']:
Ned Deily4a96a372013-01-29 00:08:32 -0800621 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000622 sysfw = os.path.join(SDKPATH, 'System', fwpth)
Ned Deily4a96a372013-01-29 00:08:32 -0800623 libfw = os.path.join(SDKPATH, fwpth)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000624 usrfw = os.path.join(os.getenv('HOME'), fwpth)
Ned Deily4a96a372013-01-29 00:08:32 -0800625 frameworks[framework] = os.readlink(sysfw)
626 if not os.path.exists(libfw):
627 fatal("Please install a link to a current %s %s as %s so "
628 "the user can override the system framework."
629 % (framework, frameworks[framework], libfw))
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000630 if os.readlink(libfw) != os.readlink(sysfw):
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000631 fatal("Version of %s must match %s" % (libfw, sysfw) )
632 if os.path.exists(usrfw):
633 fatal("Please rename %s to avoid possible dynamic load issues."
634 % usrfw)
635
Ned Deily4a96a372013-01-29 00:08:32 -0800636 if frameworks['Tcl'] != frameworks['Tk']:
637 fatal("The Tcl and Tk frameworks are not the same version.")
638
639 # add files to check after build
640 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
641 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
642 % frameworks['Tcl'],
643 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
644 % frameworks['Tk'],
645 ]
646
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000647 # Remove inherited environment variables which might influence build
648 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
649 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
650 for ev in list(os.environ):
651 for prefix in environ_var_prefixes:
652 if ev.startswith(prefix) :
Ned Deily4a96a372013-01-29 00:08:32 -0800653 print("INFO: deleting environment variable %s=%s" % (
654 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000655 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000656
Ned Deily4a96a372013-01-29 00:08:32 -0800657 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
658 if 'SDK_TOOLS_BIN' in os.environ:
659 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
660 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
661 # add its fixed location here if it exists
662 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
663 if os.path.isdir(OLD_DEVELOPER_TOOLS):
664 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
665 os.environ['PATH'] = base_path
666 print("Setting default PATH: %s"%(os.environ['PATH']))
Ned Deily7e60f512014-04-07 12:10:21 -0700667 # Ensure ws have access to hg and to sphinx-build.
668 # You may have to create links in /usr/bin for them.
669 runCommand('hg --version')
Ned Deily1ff32a92014-09-05 15:57:05 -0700670 runCommand('sphinx-build --version')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000671
Thomas Wouters89f507f2006-12-13 04:49:30 +0000672def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000673 """
674 Parse arguments and update global settings.
675 """
Ronald Oussoren1943f862009-03-30 19:39:14 +0000676 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
Ned Deily4a96a372013-01-29 00:08:32 -0800677 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily5d3febf2014-12-13 00:17:46 -0800678 global FW_VERSION_PREFIX
Thomas Wouters477c8d52006-05-27 19:21:47 +0000679
680 if args is None:
681 args = sys.argv[1:]
682
683 try:
684 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000685 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
686 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily4a96a372013-01-29 00:08:32 -0800687 except getopt.GetoptError:
688 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000689 sys.exit(1)
690
691 if args:
Ned Deily4a96a372013-01-29 00:08:32 -0800692 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000693 sys.exit(1)
694
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000695 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000696 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000697 if k in ('-h', '-?', '--help'):
Ned Deily4a96a372013-01-29 00:08:32 -0800698 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000699 sys.exit(0)
700
701 elif k in ('-d', '--build-dir'):
702 WORKDIR=v
703
704 elif k in ('--third-party',):
705 DEPSRC=v
706
707 elif k in ('--sdk-path',):
708 SDKPATH=v
709
710 elif k in ('--src-dir',):
711 SRCDIR=v
712
Ronald Oussoren1943f862009-03-30 19:39:14 +0000713 elif k in ('--dep-target', ):
714 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000715 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000716
717 elif k in ('--universal-archs', ):
718 if v in UNIVERSALOPTS:
719 UNIVERSALARCHS = v
720 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000721 if deptarget is None:
722 # Select alternate default deployment
723 # target
724 DEPTARGET = default_target_map.get(v, '10.3')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000725 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800726 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000727
Thomas Wouters477c8d52006-05-27 19:21:47 +0000728 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800729 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000730
731 SRCDIR=os.path.abspath(SRCDIR)
732 WORKDIR=os.path.abspath(WORKDIR)
733 SDKPATH=os.path.abspath(SDKPATH)
734 DEPSRC=os.path.abspath(DEPSRC)
735
Ned Deily04cdfa12014-06-25 13:36:14 -0700736 CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000737
Ned Deily5d3febf2014-12-13 00:17:46 -0800738 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
739
740 print("-- Settings:")
741 print(" * Source directory: %s" % SRCDIR)
742 print(" * Build directory: %s" % WORKDIR)
743 print(" * SDK location: %s" % SDKPATH)
744 print(" * Third-party source: %s" % DEPSRC)
745 print(" * Deployment target: %s" % DEPTARGET)
746 print(" * Universal archs: %s" % str(ARCHLIST))
747 print(" * C compiler: %s" % CC)
748 print(" * C++ compiler: %s" % CXX)
Ned Deily4a96a372013-01-29 00:08:32 -0800749 print("")
Ned Deily5d3febf2014-12-13 00:17:46 -0800750 print(" -- Building a Python %s framework at patch level %s"
751 % (getVersion(), getFullVersion()))
752 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000753
754def extractArchive(builddir, archiveName):
755 """
756 Extract a source archive into 'builddir'. Returns the path of the
757 extracted archive.
758
759 XXX: This function assumes that archives contain a toplevel directory
760 that is has the same name as the basename of the archive. This is
Ned Deily5b3582c2013-10-25 00:41:46 -0700761 safe enough for almost anything we use. Unfortunately, it does not
762 work for current Tcl and Tk source releases where the basename of
763 the archive ends with "-src" but the uncompressed directory does not.
764 For now, just special case Tcl and Tk tar.gz downloads.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000765 """
766 curdir = os.getcwd()
767 try:
768 os.chdir(builddir)
769 if archiveName.endswith('.tar.gz'):
770 retval = os.path.basename(archiveName[:-7])
Ned Deily5b3582c2013-10-25 00:41:46 -0700771 if ((retval.startswith('tcl') or retval.startswith('tk'))
772 and retval.endswith('-src')):
773 retval = retval[:-4]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000774 if os.path.exists(retval):
775 shutil.rmtree(retval)
776 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
777
778 elif archiveName.endswith('.tar.bz2'):
779 retval = os.path.basename(archiveName[:-8])
780 if os.path.exists(retval):
781 shutil.rmtree(retval)
782 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
783
784 elif archiveName.endswith('.tar'):
785 retval = os.path.basename(archiveName[:-4])
786 if os.path.exists(retval):
787 shutil.rmtree(retval)
788 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
789
790 elif archiveName.endswith('.zip'):
791 retval = os.path.basename(archiveName[:-4])
792 if os.path.exists(retval):
793 shutil.rmtree(retval)
794 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
795
796 data = fp.read()
797 xit = fp.close()
798 if xit is not None:
799 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800800 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000801
802 return os.path.join(builddir, retval)
803
804 finally:
805 os.chdir(curdir)
806
Thomas Wouters477c8d52006-05-27 19:21:47 +0000807def downloadURL(url, fname):
808 """
809 Download the contents of the url into the file.
810 """
Ned Deily4a96a372013-01-29 00:08:32 -0800811 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000812 fpOut = open(fname, 'wb')
813 block = fpIn.read(10240)
814 try:
815 while block:
816 fpOut.write(block)
817 block = fpIn.read(10240)
818 fpIn.close()
819 fpOut.close()
820 except:
821 try:
822 os.unlink(fname)
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300823 except OSError:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000824 pass
825
Ned Deily4a96a372013-01-29 00:08:32 -0800826def verifyThirdPartyFile(url, checksum, fname):
827 """
828 Download file from url to filename fname if it does not already exist.
829 Abort if file contents does not match supplied md5 checksum.
830 """
831 name = os.path.basename(fname)
832 if os.path.exists(fname):
833 print("Using local copy of %s"%(name,))
834 else:
835 print("Did not find local copy of %s"%(name,))
836 print("Downloading %s"%(name,))
837 downloadURL(url, fname)
838 print("Archive for %s stored as %s"%(name, fname))
839 if os.system(
840 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
841 % (shellQuote(fname), checksum) ):
842 fatal('MD5 checksum mismatch for file %s' % fname)
843
Ned Deily5d3febf2014-12-13 00:17:46 -0800844def build_universal_openssl(basedir, archList):
845 """
846 Special case build recipe for universal build of openssl.
847
848 The upstream OpenSSL build system does not directly support
849 OS X universal builds. We need to build each architecture
850 separately then lipo them together into fat libraries.
851 """
852
853 # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4).
854 # If we are building on a 10.4.x or earlier system,
855 # unilaterally disable assembly code building to avoid the problem.
856 no_asm = int(platform.release().split(".")[0]) < 9
857
858 def build_openssl_arch(archbase, arch):
859 "Build one architecture of openssl"
860 arch_opts = {
861 "i386": ["darwin-i386-cc"],
862 "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
863 "ppc": ["darwin-ppc-cc"],
864 "ppc64": ["darwin64-ppc-cc"],
865 }
866 configure_opts = [
867 "no-krb5",
868 "no-idea",
869 "no-mdc2",
870 "no-rc5",
871 "no-zlib",
872 "enable-tlsext",
873 "no-ssl2",
874 "no-ssl3",
875 "no-ssl3-method",
876 # "enable-unit-test",
877 "shared",
878 "--install_prefix=%s"%shellQuote(archbase),
879 "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX),
880 "--openssldir=/System/Library/OpenSSL",
881 ]
882 if no_asm:
883 configure_opts.append("no-asm")
884 runCommand(" ".join(["perl", "Configure"]
885 + arch_opts[arch] + configure_opts))
886 runCommand("make depend OSX_SDK=%s" % SDKPATH)
887 runCommand("make all OSX_SDK=%s" % SDKPATH)
888 runCommand("make install_sw OSX_SDK=%s" % SDKPATH)
889 # runCommand("make test")
890 return
891
892 srcdir = os.getcwd()
893 universalbase = os.path.join(srcdir, "..",
894 os.path.basename(srcdir) + "-universal")
895 os.mkdir(universalbase)
896 archbasefws = []
897 for arch in archList:
898 # fresh copy of the source tree
899 archsrc = os.path.join(universalbase, arch, "src")
900 shutil.copytree(srcdir, archsrc, symlinks=True)
901 # install base for this arch
902 archbase = os.path.join(universalbase, arch, "root")
903 os.mkdir(archbase)
904 # Python framework base within install_prefix:
905 # the build will install into this framework..
906 # This is to ensure that the resulting shared libs have
907 # the desired real install paths built into them.
908 archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX)
909
910 # build one architecture
911 os.chdir(archsrc)
912 build_openssl_arch(archbase, arch)
913 os.chdir(srcdir)
914 archbasefws.append(archbasefw)
915
916 # copy arch-independent files from last build into the basedir framework
917 basefw = os.path.join(basedir, *FW_VERSION_PREFIX)
918 shutil.copytree(
919 os.path.join(archbasefw, "include", "openssl"),
920 os.path.join(basefw, "include", "openssl")
921 )
922
923 shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"),
924 "SHLIB_VERSION_NUMBER")
925 # e.g. -> "1.0.0"
926 libcrypto = "libcrypto.dylib"
927 libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".")
928 # e.g. -> "libcrypto.1.0.0.dylib"
929 libssl = "libssl.dylib"
930 libssl_versioned = libssl.replace(".", "."+shlib_version_number+".")
931 # e.g. -> "libssl.1.0.0.dylib"
932
933 try:
934 os.mkdir(os.path.join(basefw, "lib"))
935 except OSError:
936 pass
937
938 # merge the individual arch-dependent shared libs into a fat shared lib
939 archbasefws.insert(0, basefw)
940 for (lib_unversioned, lib_versioned) in [
941 (libcrypto, libcrypto_versioned),
942 (libssl, libssl_versioned)
943 ]:
944 runCommand("lipo -create -output " +
945 " ".join(shellQuote(
946 os.path.join(fw, "lib", lib_versioned))
947 for fw in archbasefws))
948 # and create an unversioned symlink of it
949 os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned))
950
951 # Create links in the temp include and lib dirs that will be injected
952 # into the Python build so that setup.py can find them while building
953 # and the versioned links so that the setup.py post-build import test
954 # does not fail.
955 relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX)
956 for fn in [
957 ["include", "openssl"],
958 ["lib", libcrypto],
959 ["lib", libssl],
960 ["lib", libcrypto_versioned],
961 ["lib", libssl_versioned],
962 ]:
963 os.symlink(
964 os.path.join(relative_path, *fn),
965 os.path.join(basedir, "usr", "local", *fn)
966 )
967
968 return
969
Thomas Wouters477c8d52006-05-27 19:21:47 +0000970def buildRecipe(recipe, basedir, archList):
971 """
972 Build software using a recipe. This function does the
973 'configure;make;make install' dance for C software, with a possibility
974 to customize this process, basically a poor-mans DarwinPorts.
975 """
976 curdir = os.getcwd()
977
978 name = recipe['name']
Ned Deily5d3febf2014-12-13 00:17:46 -0800979 THIRD_PARTY_LIBS.append(name)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000980 url = recipe['url']
981 configure = recipe.get('configure', './configure')
Ned Deily5d3febf2014-12-13 00:17:46 -0800982 buildrecipe = recipe.get('buildrecipe', None)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000983 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
984 shellQuote(basedir)))
985
986 archiveName = os.path.split(url)[-1]
987 sourceArchive = os.path.join(DEPSRC, archiveName)
988
989 if not os.path.exists(DEPSRC):
990 os.mkdir(DEPSRC)
991
Ned Deily4a96a372013-01-29 00:08:32 -0800992 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
993 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000994 buildDir=os.path.join(WORKDIR, '_bld')
995 if not os.path.exists(buildDir):
996 os.mkdir(buildDir)
997
998 workDir = extractArchive(buildDir, sourceArchive)
999 os.chdir(workDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001000
Ned Deily4a96a372013-01-29 00:08:32 -08001001 for patch in recipe.get('patches', ()):
1002 if isinstance(patch, tuple):
1003 url, checksum = patch
1004 fn = os.path.join(DEPSRC, os.path.basename(url))
1005 verifyThirdPartyFile(url, checksum, fn)
1006 else:
1007 # patch is a file in the source directory
1008 fn = os.path.join(curdir, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001009 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
1010 shellQuote(fn),))
1011
Ned Deily4a96a372013-01-29 00:08:32 -08001012 for patchscript in recipe.get('patchscripts', ()):
1013 if isinstance(patchscript, tuple):
1014 url, checksum = patchscript
1015 fn = os.path.join(DEPSRC, os.path.basename(url))
1016 verifyThirdPartyFile(url, checksum, fn)
1017 else:
1018 # patch is a file in the source directory
1019 fn = os.path.join(curdir, patchscript)
1020 if fn.endswith('.bz2'):
1021 runCommand('bunzip2 -fk %s' % shellQuote(fn))
1022 fn = fn[:-4]
1023 runCommand('sh %s' % shellQuote(fn))
1024 os.unlink(fn)
1025
Ned Deily94764b22013-10-27 19:49:29 -07001026 if 'buildDir' in recipe:
1027 os.chdir(recipe['buildDir'])
1028
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001029 if configure is not None:
1030 configure_args = [
1031 "--prefix=/usr/local",
1032 "--enable-static",
1033 "--disable-shared",
1034 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
1035 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001036
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001037 if 'configure_pre' in recipe:
1038 args = list(recipe['configure_pre'])
1039 if '--disable-static' in args:
1040 configure_args.remove('--enable-static')
1041 if '--enable-shared' in args:
1042 configure_args.remove('--disable-shared')
1043 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001044
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001045 if recipe.get('useLDFlags', 1):
1046 configure_args.extend([
Ned Deily4a96a372013-01-29 00:08:32 -08001047 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
1048 "-I%s/usr/local/include"%(
1049 recipe.get('extra_cflags', ''),
1050 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001051 ' -arch '.join(archList),
1052 shellQuote(SDKPATH)[1:-1],
1053 shellQuote(basedir)[1:-1],),
Ned Deily0134a352014-04-09 16:16:08 -07001054 "LDFLAGS=-mmacosx-version-min=%s -isysroot %s -L%s/usr/local/lib -arch %s"%(
Ned Deily4a96a372013-01-29 00:08:32 -08001055 DEPTARGET,
Thomas Wouters477c8d52006-05-27 19:21:47 +00001056 shellQuote(SDKPATH)[1:-1],
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001057 shellQuote(basedir)[1:-1],
1058 ' -arch '.join(archList)),
1059 ])
1060 else:
1061 configure_args.extend([
Ned Deily4a96a372013-01-29 00:08:32 -08001062 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
1063 "-I%s/usr/local/include"%(
1064 recipe.get('extra_cflags', ''),
1065 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001066 ' -arch '.join(archList),
1067 shellQuote(SDKPATH)[1:-1],
1068 shellQuote(basedir)[1:-1],),
1069 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001070
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001071 if 'configure_post' in recipe:
Ned Deily4a96a372013-01-29 00:08:32 -08001072 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001073
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001074 configure_args.insert(0, configure)
1075 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001076
Ned Deily4a96a372013-01-29 00:08:32 -08001077 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001078 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001079
Ned Deily5d3febf2014-12-13 00:17:46 -08001080 if buildrecipe is not None:
1081 # call special-case build recipe, e.g. for openssl
1082 buildrecipe(basedir, archList)
1083
1084 if install is not None:
1085 print("Running install for %s"%(name,))
1086 runCommand('{ ' + install + ' ;} 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001087
Ned Deily4a96a372013-01-29 00:08:32 -08001088 print("Done %s"%(name,))
1089 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001090
1091 os.chdir(curdir)
1092
1093def buildLibraries():
1094 """
1095 Build our dependencies into $WORKDIR/libraries/usr/local
1096 """
Ned Deily4a96a372013-01-29 00:08:32 -08001097 print("")
1098 print("Building required libraries")
1099 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001100 universal = os.path.join(WORKDIR, 'libraries')
1101 os.mkdir(universal)
1102 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
1103 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
1104
Ronald Oussoren1943f862009-03-30 19:39:14 +00001105 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001106 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001107
1108
1109
1110def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001111 # This stores the documentation as Resources/English.lproj/Documentation
Thomas Wouters477c8d52006-05-27 19:21:47 +00001112 # inside the framwork. pydoc and IDLE will pick it up there.
Ned Deily4a96a372013-01-29 00:08:32 -08001113 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001114 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001115 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001116 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001117 curDir = os.getcwd()
1118 os.chdir(buildDir)
Ned Deily1ff32a92014-09-05 15:57:05 -07001119 # The Doc build changed for 3.4 (technically, for 3.4.1) and for 2.7.9
1120 runCommand('make clean')
1121 # Assume sphinx-build is on our PATH, checked in checkEnvironment
1122 runCommand('make html')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001123 os.chdir(curDir)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001124 if not os.path.exists(docdir):
1125 os.mkdir(docdir)
Ronald Oussorenf84d7e92009-05-19 11:27:25 +00001126 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001127
1128
1129def buildPython():
Ned Deily4a96a372013-01-29 00:08:32 -08001130 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001131
1132 buildDir = os.path.join(WORKDIR, '_bld', 'python')
1133 rootDir = os.path.join(WORKDIR, '_root')
1134
1135 if os.path.exists(buildDir):
1136 shutil.rmtree(buildDir)
1137 if os.path.exists(rootDir):
1138 shutil.rmtree(rootDir)
Ned Deily4f7ff782011-01-15 05:29:12 +00001139 os.makedirs(buildDir)
1140 os.makedirs(rootDir)
1141 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001142 curdir = os.getcwd()
1143 os.chdir(buildDir)
1144
1145 # Not sure if this is still needed, the original build script
1146 # claims that parts of the install assume python.exe exists.
1147 os.symlink('python', os.path.join(buildDir, 'python.exe'))
1148
1149 # Extract the version from the configure file, needed to calculate
1150 # several paths.
1151 version = getVersion()
1152
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001153 # Since the extra libs are not in their installed framework location
1154 # during the build, augment the library path so that the interpreter
1155 # will find them during its extension import sanity checks.
1156 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
1157 'libraries', 'usr', 'local', 'lib')
Ned Deily4a96a372013-01-29 00:08:32 -08001158 print("Running configure...")
Ronald Oussoren1943f862009-03-30 19:39:14 +00001159 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001160 "--with-universal-archs=%s "
1161 "%s "
Ned Deily41ab6c32013-11-22 22:25:43 -08001162 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +00001163 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deily4b7a0232013-10-25 00:46:02 -07001164 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001165 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
1166 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001167 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deilyed730102014-11-14 18:55:05 -08001168 (' ', '--without-ensurepip ')[PYTHON_3],
Ronald Oussoren1943f862009-03-30 19:39:14 +00001169 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +00001170 shellQuote(WORKDIR)[1:-1]))
1171
Ned Deily729148b2014-05-22 15:28:06 -07001172 print("Running make touch")
1173 runCommand("make touch")
1174
Ned Deily4a96a372013-01-29 00:08:32 -08001175 print("Running make")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001176 runCommand("make")
1177
Ned Deily4a96a372013-01-29 00:08:32 -08001178 print("Running make install")
Ronald Oussorenf84d7e92009-05-19 11:27:25 +00001179 runCommand("make install DESTDIR=%s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001180 shellQuote(rootDir)))
1181
Ned Deily4a96a372013-01-29 00:08:32 -08001182 print("Running make frameworkinstallextras")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001183 runCommand("make frameworkinstallextras DESTDIR=%s"%(
1184 shellQuote(rootDir)))
1185
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001186 del os.environ['DYLD_LIBRARY_PATH']
Ned Deily4a96a372013-01-29 00:08:32 -08001187 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001188 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
1189 runCommand("mv %s/* %s"%(
1190 shellQuote(os.path.join(
1191 WORKDIR, 'libraries', 'Library', 'Frameworks',
1192 'Python.framework', 'Versions', getVersion(),
1193 'lib')),
1194 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
1195 'Python.framework', 'Versions', getVersion(),
1196 'lib'))))
1197
Ned Deily050fcd52013-10-26 03:16:44 -07001198 path_to_lib = os.path.join(rootDir, 'Library', 'Frameworks',
1199 'Python.framework', 'Versions',
1200 version, 'lib', 'python%s'%(version,))
1201
Ned Deily4a96a372013-01-29 00:08:32 -08001202 print("Fix file modes")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001203 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
Thomas Wouters89f507f2006-12-13 04:49:30 +00001204 gid = grp.getgrnam('admin').gr_gid
1205
Ned Deily4a96a372013-01-29 00:08:32 -08001206 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00001207 for dirpath, dirnames, filenames in os.walk(frmDir):
1208 for dn in dirnames:
Ned Deily4a96a372013-01-29 00:08:32 -08001209 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001210 os.chown(os.path.join(dirpath, dn), -1, gid)
1211
Thomas Wouters477c8d52006-05-27 19:21:47 +00001212 for fn in filenames:
1213 if os.path.islink(fn):
1214 continue
1215
1216 # "chmod g+w $fn"
1217 p = os.path.join(dirpath, fn)
1218 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001219 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1220 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001221
Ned Deily4a96a372013-01-29 00:08:32 -08001222 if fn in EXPECTED_SHARED_LIBS:
1223 # check to see that this file was linked with the
1224 # expected library path and version
1225 data = captureCommand("otool -L %s" % shellQuote(p))
1226 for sl in EXPECTED_SHARED_LIBS[fn]:
1227 if ("\t%s " % sl) not in data:
1228 print("Expected shared lib %s was not linked with %s"
1229 % (sl, p))
1230 shared_lib_error = True
1231
1232 if shared_lib_error:
1233 fatal("Unexpected shared library errors.")
1234
Ned Deilye59e4c52011-01-29 18:56:28 +00001235 if PYTHON_3:
1236 LDVERSION=None
1237 VERSION=None
1238 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001239
Ned Deilye59e4c52011-01-29 18:56:28 +00001240 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001241 for ln in fp:
1242 if ln.startswith('VERSION='):
1243 VERSION=ln.split()[1]
1244 if ln.startswith('ABIFLAGS='):
1245 ABIFLAGS=ln.split()[1]
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001246 if ln.startswith('LDVERSION='):
1247 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +00001248 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001249
Ned Deilye59e4c52011-01-29 18:56:28 +00001250 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1251 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1252 config_suffix = '-' + LDVERSION
Ned Deily652bad42016-08-15 14:37:14 -04001253 if getVersionMajorMinor() >= (3, 6):
1254 config_suffix = config_suffix + '-darwin'
Ned Deilye59e4c52011-01-29 18:56:28 +00001255 else:
1256 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001257
Thomas Wouters477c8d52006-05-27 19:21:47 +00001258 # We added some directories to the search path during the configure
1259 # phase. Remove those because those directories won't be there on
Ned Deily4a96a372013-01-29 00:08:32 -08001260 # the end-users system. Also remove the directories from _sysconfigdata.py
1261 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001262
Ned Deilya4f6b002013-10-25 00:47:38 -07001263 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1264 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1265
Ned Deilya4f6b002013-10-25 00:47:38 -07001266 # fix Makefile
1267 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1268 fp = open(path, 'r')
1269 data = fp.read()
1270 fp.close()
1271
1272 for p in (include_path, lib_path):
1273 data = data.replace(" " + p, '')
1274 data = data.replace(p + " ", '')
1275
1276 fp = open(path, 'w')
1277 fp.write(data)
1278 fp.close()
1279
Ned Deily652bad42016-08-15 14:37:14 -04001280 # fix _sysconfigdata
Ned Deilya4f6b002013-10-25 00:47:38 -07001281 #
1282 # TODO: make this more robust! test_sysconfig_module of
1283 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1284 # the output from get_config_var in both sysconfig and
1285 # distutils.sysconfig is exactly the same for both CFLAGS and
1286 # LDFLAGS. The fixing up is now complicated by the pretty
1287 # printing in _sysconfigdata.py. Also, we are using the
1288 # pprint from the Python running the installer build which
1289 # may not cosmetically format the same as the pprint in the Python
1290 # being built (and which is used to originally generate
1291 # _sysconfigdata.py).
1292
1293 import pprint
Ned Deily652bad42016-08-15 14:37:14 -04001294 if getVersionMajorMinor() >= (3, 6):
Zachary Warec4b53af2016-09-09 17:59:49 -07001295 # XXX this is extra-fragile
1296 path = os.path.join(path_to_lib, '_sysconfigdata_m_darwin_darwin.py')
Ned Deily652bad42016-08-15 14:37:14 -04001297 else:
1298 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1299 fp = open(path, 'r')
1300 data = fp.read()
1301 fp.close()
1302 # create build_time_vars dict
1303 exec(data)
1304 vars = {}
1305 for k, v in build_time_vars.items():
1306 if type(v) == type(''):
1307 for p in (include_path, lib_path):
1308 v = v.replace(' ' + p, '')
1309 v = v.replace(p + ' ', '')
1310 vars[k] = v
Ned Deily4a96a372013-01-29 00:08:32 -08001311
Ned Deily652bad42016-08-15 14:37:14 -04001312 fp = open(path, 'w')
1313 # duplicated from sysconfig._generate_posix_vars()
1314 fp.write('# system configuration generated and used by'
1315 ' the sysconfig module\n')
1316 fp.write('build_time_vars = ')
1317 pprint.pprint(vars, stream=fp)
1318 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001319
1320 # Add symlinks in /usr/local/bin, using relative links
1321 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1322 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1323 'Python.framework', 'Versions', version, 'bin')
1324 if os.path.exists(usr_local_bin):
1325 shutil.rmtree(usr_local_bin)
1326 os.makedirs(usr_local_bin)
1327 for fn in os.listdir(
1328 os.path.join(frmDir, 'Versions', version, 'bin')):
1329 os.symlink(os.path.join(to_framework, fn),
1330 os.path.join(usr_local_bin, fn))
1331
1332 os.chdir(curdir)
1333
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001334 if PYTHON_3:
Ezio Melotti7c4a7e62013-08-26 01:32:56 +03001335 # Remove the 'Current' link, that way we don't accidentally mess
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001336 # with an already installed version of python 2
1337 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1338 'Python.framework', 'Versions', 'Current'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001339
1340def patchFile(inPath, outPath):
1341 data = fileContents(inPath)
1342 data = data.replace('$FULL_VERSION', getFullVersion())
1343 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001344 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001345 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001346 data = data.replace('$INSTALL_SIZE', installSize())
Ned Deily5d3febf2014-12-13 00:17:46 -08001347 data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001348
1349 # This one is not handy as a template variable
1350 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily4a96a372013-01-29 00:08:32 -08001351 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001352 fp.write(data)
1353 fp.close()
1354
1355def patchScript(inPath, outPath):
Ned Deilyed730102014-11-14 18:55:05 -08001356 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001357 data = fileContents(inPath)
Ned Deilyed730102014-11-14 18:55:05 -08001358 data = data.replace('@PYMAJOR@', str(major))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001359 data = data.replace('@PYVER@', getVersion())
Ned Deily4a96a372013-01-29 00:08:32 -08001360 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001361 fp.write(data)
1362 fp.close()
Ned Deily4a96a372013-01-29 00:08:32 -08001363 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001364
1365
1366
1367def packageFromRecipe(targetDir, recipe):
1368 curdir = os.getcwd()
1369 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001370 # The major version (such as 2.5) is included in the package name
1371 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001372 # common.
1373 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001374 srcdir = recipe.get('source')
1375 pkgroot = recipe.get('topdir', srcdir)
1376 postflight = recipe.get('postflight')
1377 readme = textwrap.dedent(recipe['readme'])
1378 isRequired = recipe.get('required', True)
1379
Ned Deily4a96a372013-01-29 00:08:32 -08001380 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001381
1382 # Substitute some variables
1383 textvars = dict(
1384 VER=getVersion(),
1385 FULLVER=getFullVersion(),
1386 )
1387 readme = readme % textvars
1388
1389 if pkgroot is not None:
1390 pkgroot = pkgroot % textvars
1391 else:
1392 pkgroot = '/'
1393
1394 if srcdir is not None:
1395 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1396 srcdir = srcdir % textvars
1397
1398 if postflight is not None:
1399 postflight = os.path.abspath(postflight)
1400
1401 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1402 os.makedirs(packageContents)
1403
1404 if srcdir is not None:
1405 os.chdir(srcdir)
1406 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1407 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1408 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1409
1410 fn = os.path.join(packageContents, 'PkgInfo')
1411 fp = open(fn, 'w')
1412 fp.write('pmkrpkg1')
1413 fp.close()
1414
1415 rsrcDir = os.path.join(packageContents, "Resources")
1416 os.mkdir(rsrcDir)
1417 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1418 fp.write(readme)
1419 fp.close()
1420
1421 if postflight is not None:
1422 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1423
1424 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001425 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001426 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001427 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1428 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1429 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001430 CFBundleShortVersionString=vers,
1431 IFMajorVersion=major,
1432 IFMinorVersion=minor,
1433 IFPkgFormatVersion=0.10000000149011612,
1434 IFPkgFlagAllowBackRev=False,
1435 IFPkgFlagAuthorizationAction="RootAuthorization",
1436 IFPkgFlagDefaultLocation=pkgroot,
1437 IFPkgFlagFollowLinks=True,
1438 IFPkgFlagInstallFat=True,
1439 IFPkgFlagIsRequired=isRequired,
1440 IFPkgFlagOverwritePermissions=False,
1441 IFPkgFlagRelocatable=False,
1442 IFPkgFlagRestartAction="NoRestart",
1443 IFPkgFlagRootVolumeOnly=True,
1444 IFPkgFlagUpdateInstalledLangauges=False,
1445 )
1446 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1447
1448 pl = Plist(
1449 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001450 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001451 IFPkgDescriptionVersion=vers,
1452 )
1453 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1454
1455 finally:
1456 os.chdir(curdir)
1457
1458
1459def makeMpkgPlist(path):
1460
1461 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001462 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001463
1464 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001465 CFBundleGetInfoString="Python %s"%(vers,),
1466 CFBundleIdentifier='org.python.Python',
1467 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001468 CFBundleShortVersionString=vers,
1469 IFMajorVersion=major,
1470 IFMinorVersion=minor,
1471 IFPkgFlagComponentDirectory="Contents/Packages",
1472 IFPkgFlagPackageList=[
1473 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001474 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001475 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001476 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001477 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001478 ],
1479 IFPkgFormatVersion=0.10000000149011612,
1480 IFPkgFlagBackgroundScaling="proportional",
1481 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001482 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001483 )
1484
1485 writePlist(pl, path)
1486
1487
1488def buildInstaller():
1489
1490 # Zap all compiled files
1491 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1492 for fn in filenames:
1493 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1494 os.unlink(os.path.join(dirpath, fn))
1495
1496 outdir = os.path.join(WORKDIR, 'installer')
1497 if os.path.exists(outdir):
1498 shutil.rmtree(outdir)
1499 os.mkdir(outdir)
1500
Ronald Oussoren1943f862009-03-30 19:39:14 +00001501 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001502 pkgcontents = os.path.join(pkgroot, 'Packages')
1503 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001504 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001505 packageFromRecipe(pkgcontents, recipe)
1506
1507 rsrcDir = os.path.join(pkgroot, 'Resources')
1508
1509 fn = os.path.join(pkgroot, 'PkgInfo')
1510 fp = open(fn, 'w')
1511 fp.write('pmkrpkg1')
1512 fp.close()
1513
1514 os.mkdir(rsrcDir)
1515
1516 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1517 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001518 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001519 IFPkgDescriptionVersion=getVersion(),
1520 )
1521
1522 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1523 for fn in os.listdir('resources'):
1524 if fn == '.svn': continue
1525 if fn.endswith('.jpg'):
1526 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1527 else:
1528 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1529
Thomas Wouters477c8d52006-05-27 19:21:47 +00001530
1531def installSize(clear=False, _saved=[]):
1532 if clear:
1533 del _saved[:]
1534 if not _saved:
1535 data = captureCommand("du -ks %s"%(
1536 shellQuote(os.path.join(WORKDIR, '_root'))))
1537 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1538 return _saved[0]
1539
1540
1541def buildDMG():
1542 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001543 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001544 """
1545 outdir = os.path.join(WORKDIR, 'diskimage')
1546 if os.path.exists(outdir):
1547 shutil.rmtree(outdir)
1548
1549 imagepath = os.path.join(outdir,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001550 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001551 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001552 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001553 imagepath = imagepath + '.dmg'
1554
1555 os.mkdir(outdir)
Ronald Oussoren1943f862009-03-30 19:39:14 +00001556 volname='Python %s'%(getFullVersion())
1557 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1558 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001559 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001560 shellQuote(imagepath + ".tmp.dmg" )))
1561
1562
1563 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1564 os.mkdir(os.path.join(WORKDIR, "mnt"))
1565 runCommand("hdiutil attach %s -mountroot %s"%(
1566 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1567
1568 # Custom icon for the DMG, shown when the DMG is mounted.
1569 shutil.copy("../Icons/Disk Image.icns",
1570 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deily4a96a372013-01-29 00:08:32 -08001571 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001572 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1573
1574 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1575
1576 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1577 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1578 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1579 setIcon(imagepath, "../Icons/Disk Image.icns")
1580
1581 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001582
1583 return imagepath
1584
1585
1586def setIcon(filePath, icnsPath):
1587 """
1588 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001589 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001590
Ronald Oussoren70050672010-04-30 15:00:26 +00001591 dirPath = os.path.normpath(os.path.dirname(__file__))
1592 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001593 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1594 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1595 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001596 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1597 if not os.path.exists(appPath):
1598 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001599 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1600 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001601
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001602 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1603 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001604
1605def main():
1606 # First parse options and check if we can perform our work
1607 parseOptions()
1608 checkEnvironment()
1609
Ronald Oussoren1943f862009-03-30 19:39:14 +00001610 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001611 os.environ['CC'] = CC
Ned Deily4a96a372013-01-29 00:08:32 -08001612 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001613
1614 if os.path.exists(WORKDIR):
1615 shutil.rmtree(WORKDIR)
1616 os.mkdir(WORKDIR)
1617
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001618 os.environ['LC_ALL'] = 'C'
1619
Thomas Wouters477c8d52006-05-27 19:21:47 +00001620 # Then build third-party libraries such as sleepycat DB4.
1621 buildLibraries()
1622
1623 # Now build python itself
1624 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001625
1626 # And then build the documentation
1627 # Remove the Deployment Target from the shell
1628 # environment, it's no longer needed and
1629 # an unexpected build target can cause problems
1630 # when Sphinx and its dependencies need to
1631 # be (re-)installed.
1632 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001633 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001634
1635
1636 # Prepare the applications folder
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001637 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001638 getVersion(),))
Ned Deily5d3febf2014-12-13 00:17:46 -08001639 fn = os.path.join(folder, "License.rtf")
1640 patchFile("resources/License.rtf", fn)
1641 fn = os.path.join(folder, "ReadMe.rtf")
1642 patchFile("resources/ReadMe.rtf", fn)
1643 fn = os.path.join(folder, "Update Shell Profile.command")
1644 patchScript("scripts/postflight.patch-profile", fn)
Ned Deily4a96a372013-01-29 00:08:32 -08001645 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001646 setIcon(folder, "../Icons/Python Folder.icns")
1647
1648 # Create the installer
1649 buildInstaller()
1650
1651 # And copy the readme into the directory containing the installer
Ned Deily5d3febf2014-12-13 00:17:46 -08001652 patchFile('resources/ReadMe.rtf',
1653 os.path.join(WORKDIR, 'installer', 'ReadMe.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001654
1655 # Ditto for the license file.
Ned Deily5d3febf2014-12-13 00:17:46 -08001656 patchFile('resources/License.rtf',
1657 os.path.join(WORKDIR, 'installer', 'License.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001658
1659 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily4a96a372013-01-29 00:08:32 -08001660 fp.write("# BUILD INFO\n")
1661 fp.write("# Date: %s\n" % time.ctime())
1662 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001663 fp.close()
1664
Thomas Wouters477c8d52006-05-27 19:21:47 +00001665 # And copy it to a DMG
1666 buildDMG()
1667
Thomas Wouters477c8d52006-05-27 19:21:47 +00001668if __name__ == "__main__":
1669 main()