blob: bff4eae31b4b58c56b007ac4b334792a3a4c1eda [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
Ned Deilyb364d9f2017-07-24 04:58:43 -040016requires an installed third-party version of
Ned Deily4a96a372013-01-29 00:08:32 -080017Tcl/Tk 8.4 (for OS X 10.4 and 10.5 deployment targets) or Tcl/TK 8.5
18(for 10.6 or later) installed in /Library/Frameworks. When installed,
19the Python built by this script will attempt to dynamically link first to
20Tcl and Tk frameworks in /Library/Frameworks if available otherwise fall
21back to the ones in /System/Library/Framework. For the build, we recommend
22installing the most recent ActiveTcl 8.4 or 8.5 version.
23
2432-bit-only installer builds are still possible on OS X 10.4 with Xcode 2.5
25and the installation of additional components, such as a newer Python
Ned Deilyb364d9f2017-07-24 04:58:43 -040026(2.5 is needed for Python parser updates) and for the documentation
Ned Deily9978a932014-04-09 16:15:20 -070027build 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
Ned Deilydde4f632016-09-12 09:39:23 -0400104FW_SSL_DIRECTORY = "--undefined--" # initialized in parseOptions
Ned Deily5d3febf2014-12-13 00:17:46 -0800105
Thomas Wouters89f507f2006-12-13 04:49:30 +0000106# The directory we'll use to create the build (will be erased and recreated)
107WORKDIR = "/tmp/_py"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000108
Thomas Wouters89f507f2006-12-13 04:49:30 +0000109# The directory we'll use to store third-party sources. Set this to something
Thomas Wouters477c8d52006-05-27 19:21:47 +0000110# else if you don't want to re-fetch required libraries every time.
Thomas Wouters89f507f2006-12-13 04:49:30 +0000111DEPSRC = os.path.join(WORKDIR, 'third-party')
112DEPSRC = os.path.expanduser('~/Universal/other-sources')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000113
114# Location of the preferred SDK
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000115
116### There are some issues with the SDK selection below here,
117### The resulting binary doesn't work on all platforms that
118### it should. Always default to the 10.4u SDK until that
Ezio Melotti7c4a7e62013-08-26 01:32:56 +0300119### issue is resolved.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000120###
121##if int(os.uname()[2].split('.')[0]) == 8:
122## # Explicitly use the 10.4u (universal) SDK when
123## # building on 10.4, the system headers are not
124## # useable for a universal build
125## SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk"
126##else:
127## SDKPATH = "/"
128
Thomas Wouters89f507f2006-12-13 04:49:30 +0000129SDKPATH = "/Developer/SDKs/MacOSX10.4u.sdk"
Thomas Wouters477c8d52006-05-27 19:21:47 +0000130
Ronald Oussoren1943f862009-03-30 19:39:14 +0000131universal_opts_map = { '32-bit': ('i386', 'ppc',),
132 '64-bit': ('x86_64', 'ppc64',),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000133 'intel': ('i386', 'x86_64'),
134 '3-way': ('ppc', 'i386', 'x86_64'),
135 'all': ('i386', 'ppc', 'x86_64', 'ppc64',) }
136default_target_map = {
137 '64-bit': '10.5',
138 '3-way': '10.5',
139 'intel': '10.5',
140 'all': '10.5',
141}
Ronald Oussoren1943f862009-03-30 19:39:14 +0000142
143UNIVERSALOPTS = tuple(universal_opts_map.keys())
144
145UNIVERSALARCHS = '32-bit'
146
147ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000148
Ezio Melotti42da6632011-03-15 05:18:48 +0200149# Source directory (assume we're in Mac/BuildScript)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000150SRCDIR = os.path.dirname(
Thomas Wouters477c8d52006-05-27 19:21:47 +0000151 os.path.dirname(
152 os.path.dirname(
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000153 os.path.abspath(__file__
154 ))))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000155
Ronald Oussoren1943f862009-03-30 19:39:14 +0000156# $MACOSX_DEPLOYMENT_TARGET -> minimum OS X level
157DEPTARGET = '10.3'
158
Ned Deily04cdfa12014-06-25 13:36:14 -0700159def getDeptargetTuple():
160 return tuple([int(n) for n in DEPTARGET.split('.')[0:2]])
161
162def getTargetCompilers():
163 target_cc_map = {
Ned Deily4a96a372013-01-29 00:08:32 -0800164 '10.3': ('gcc-4.0', 'g++-4.0'),
165 '10.4': ('gcc-4.0', 'g++-4.0'),
166 '10.5': ('gcc-4.2', 'g++-4.2'),
167 '10.6': ('gcc-4.2', 'g++-4.2'),
Ned Deily04cdfa12014-06-25 13:36:14 -0700168 }
169 return target_cc_map.get(DEPTARGET, ('clang', 'clang++') )
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000170
Ned Deily04cdfa12014-06-25 13:36:14 -0700171CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000172
Ned Deily5d3febf2014-12-13 00:17:46 -0800173PYTHON_3 = getVersionMajorMinor() >= (3, 0)
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000174
Thomas Wouters89f507f2006-12-13 04:49:30 +0000175USAGE = textwrap.dedent("""\
Thomas Wouters477c8d52006-05-27 19:21:47 +0000176 Usage: build_python [options]
177
178 Options:
179 -? or -h: Show this message
180 -b DIR
181 --build-dir=DIR: Create build here (default: %(WORKDIR)r)
182 --third-party=DIR: Store third-party sources here (default: %(DEPSRC)r)
183 --sdk-path=DIR: Location of the SDK (default: %(SDKPATH)r)
184 --src-dir=DIR: Location of the Python sources (default: %(SRCDIR)r)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000185 --dep-target=10.n OS X deployment target (default: %(DEPTARGET)r)
186 --universal-archs=x universal architectures (options: %(UNIVERSALOPTS)r, default: %(UNIVERSALARCHS)r)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000187""")% globals()
188
Ned Deily4a96a372013-01-29 00:08:32 -0800189# Dict of object file names with shared library names to check after building.
190# This is to ensure that we ended up dynamically linking with the shared
191# library paths and versions we expected. For example:
192# EXPECTED_SHARED_LIBS['_tkinter.so'] = [
193# '/Library/Frameworks/Tcl.framework/Versions/8.5/Tcl',
194# '/Library/Frameworks/Tk.framework/Versions/8.5/Tk']
195EXPECTED_SHARED_LIBS = {}
Thomas Wouters477c8d52006-05-27 19:21:47 +0000196
Ned Deily5d3febf2014-12-13 00:17:46 -0800197# List of names of third party software built with this installer.
198# The names will be inserted into the rtf version of the License.
199THIRD_PARTY_LIBS = []
200
Thomas Wouters477c8d52006-05-27 19:21:47 +0000201# Instructions for building libraries that are necessary for building a
202# batteries included python.
Ronald Oussoren1943f862009-03-30 19:39:14 +0000203# [The recipes are defined here for convenience but instantiated later after
204# command line options have been processed.]
205def library_recipes():
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000206 result = []
Thomas Wouters477c8d52006-05-27 19:21:47 +0000207
Ned Deily04cdfa12014-06-25 13:36:14 -0700208 LT_10_5 = bool(getDeptargetTuple() < (10, 5))
Ned Deily4a96a372013-01-29 00:08:32 -0800209
Ned Deilydde4f632016-09-12 09:39:23 -0400210 # Since Apple removed the header files for the deprecated system
211 # OpenSSL as of the Xcode 7 release (for OS X 10.10+), we do not
212 # have much choice but to build our own copy here, too.
Ned Deily5d3febf2014-12-13 00:17:46 -0800213
Ned Deilydde4f632016-09-12 09:39:23 -0400214 result.extend([
Ned Deily5d3febf2014-12-13 00:17:46 -0800215 dict(
Ned Deily24e04692017-12-04 22:24:41 -0500216 name="OpenSSL 1.0.2m",
217 url="https://www.openssl.org/source/openssl-1.0.2m.tar.gz",
218 checksum='10e9e37f492094b9ef296f68f24a7666',
Ned Deily5d3febf2014-12-13 00:17:46 -0800219 patches=[
220 "openssl_sdk_makedepend.patch",
221 ],
222 buildrecipe=build_universal_openssl,
223 configure=None,
224 install=None,
225 ),
Ned Deilydde4f632016-09-12 09:39:23 -0400226 ])
Ned Deily5d3febf2014-12-13 00:17:46 -0800227
Ned Deilyaa6a2122013-11-23 03:30:11 -0800228# Disable for now
Ned Deilyed730102014-11-14 18:55:05 -0800229 if False: # if getDeptargetTuple() > (10, 5):
Ned Deily5b3582c2013-10-25 00:41:46 -0700230 result.extend([
231 dict(
232 name="Tcl 8.5.15",
233 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_5/tcl8.5.15-src.tar.gz",
234 checksum='f3df162f92c69b254079c4d0af7a690f',
235 buildDir="unix",
236 configure_pre=[
237 '--enable-shared',
238 '--enable-threads',
239 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
240 ],
241 useLDFlags=False,
242 install='make TCL_LIBRARY=%(TCL_LIBRARY)s && make install TCL_LIBRARY=%(TCL_LIBRARY)s DESTDIR=%(DESTDIR)s'%{
243 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
244 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.5'%(getVersion())),
245 },
246 ),
247 dict(
248 name="Tk 8.5.15",
249 url="ftp://ftp.tcl.tk/pub/tcl//tcl8_5/tk8.5.15-src.tar.gz",
250 checksum='55b8e33f903210a4e1c8bce0f820657f',
Ned Deily94764b22013-10-27 19:49:29 -0700251 patches=[
252 "issue19373_tk_8_5_15_source.patch",
253 ],
Ned Deily5b3582c2013-10-25 00:41:46 -0700254 buildDir="unix",
255 configure_pre=[
256 '--enable-aqua',
257 '--enable-shared',
258 '--enable-threads',
259 '--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib'%(getVersion(),),
260 ],
261 useLDFlags=False,
262 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'%{
263 "DESTDIR": shellQuote(os.path.join(WORKDIR, 'libraries')),
264 "TCL_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tcl8.5'%(getVersion())),
265 "TK_LIBRARY": shellQuote('/Library/Frameworks/Python.framework/Versions/%s/lib/tk8.5'%(getVersion())),
266 },
267 ),
268 ])
269
Ned Deilyed730102014-11-14 18:55:05 -0800270 if PYTHON_3:
Ned Deily4a96a372013-01-29 00:08:32 -0800271 result.extend([
272 dict(
Ned Deilye6f8a732017-12-04 22:55:20 -0500273 name="XZ 5.2.3",
274 url="http://tukaani.org/xz/xz-5.2.3.tar.gz",
275 checksum='ef68674fb47a8b8e741b34e429d86e9d',
Ned Deily4a96a372013-01-29 00:08:32 -0800276 configure_pre=[
277 '--disable-dependency-tracking',
278 ]
279 ),
280 ])
281
282 result.extend([
283 dict(
284 name="NCurses 5.9",
285 url="http://ftp.gnu.org/pub/gnu/ncurses/ncurses-5.9.tar.gz",
286 checksum='8cb9c412e5f2d96bc6f459aa8c6282a1',
287 configure_pre=[
288 "--enable-widec",
289 "--without-cxx",
290 "--without-cxx-binding",
291 "--without-ada",
292 "--without-curses-h",
293 "--enable-shared",
294 "--with-shared",
295 "--without-debug",
296 "--without-normal",
297 "--without-tests",
298 "--without-manpages",
299 "--datadir=/usr/share",
300 "--sysconfdir=/etc",
301 "--sharedstatedir=/usr/com",
302 "--with-terminfo-dirs=/usr/share/terminfo",
303 "--with-default-terminfo-dir=/usr/share/terminfo",
304 "--libdir=/Library/Frameworks/Python.framework/Versions/%s/lib"%(getVersion(),),
305 ],
306 patchscripts=[
307 ("ftp://invisible-island.net/ncurses//5.9/ncurses-5.9-20120616-patch.sh.bz2",
308 "f54bf02a349f96a7c4f0d00922f3a0d4"),
309 ],
310 useLDFlags=False,
311 install='make && make install DESTDIR=%s && cd %s/usr/local/lib && ln -fs ../../../Library/Frameworks/Python.framework/Versions/%s/lib/lib* .'%(
312 shellQuote(os.path.join(WORKDIR, 'libraries')),
313 shellQuote(os.path.join(WORKDIR, 'libraries')),
314 getVersion(),
315 ),
316 ),
317 dict(
Mariattaca7562a2017-12-04 18:04:49 -0800318 name="SQLite 3.21.0",
319 url="https://www.sqlite.org/2017/sqlite-autoconf-3210000.tar.gz",
320 checksum='7913de4c3126ba3c24689cb7a199ea31',
Ned Deily4a96a372013-01-29 00:08:32 -0800321 extra_cflags=('-Os '
Ned Deilyb3b07672016-09-05 17:31:14 -0700322 '-DSQLITE_ENABLE_FTS5 '
Ned Deily4a96a372013-01-29 00:08:32 -0800323 '-DSQLITE_ENABLE_FTS4 '
324 '-DSQLITE_ENABLE_FTS3_PARENTHESIS '
Ned Deily9625bf52017-12-04 21:50:29 -0500325 '-DSQLITE_ENABLE_JSON1 '
Ned Deily4a96a372013-01-29 00:08:32 -0800326 '-DSQLITE_ENABLE_RTREE '
327 '-DSQLITE_TCL=0 '
328 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
329 configure_pre=[
330 '--enable-threadsafe',
331 '--enable-shared=no',
332 '--enable-static=yes',
333 '--disable-readline',
334 '--disable-dependency-tracking',
335 ]
336 ),
337 ])
338
Ned Deily04cdfa12014-06-25 13:36:14 -0700339 if getDeptargetTuple() < (10, 5):
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000340 result.extend([
341 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000342 name="Bzip2 1.0.6",
343 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
344 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000345 configure=None,
Ned Deily4a96a372013-01-29 00:08:32 -0800346 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
347 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000348 shellQuote(os.path.join(WORKDIR, 'libraries')),
349 ' -arch '.join(ARCHLIST),
350 SDKPATH,
351 ),
352 ),
353 dict(
354 name="ZLib 1.2.3",
355 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
356 checksum='debc62758716a169df9f62e6ab2bc634',
357 configure=None,
Ned Deily4a96a372013-01-29 00:08:32 -0800358 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
359 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000360 shellQuote(os.path.join(WORKDIR, 'libraries')),
361 ' -arch '.join(ARCHLIST),
362 SDKPATH,
363 ),
364 ),
365 dict(
366 # Note that GNU readline is GPL'd software
Ned Deily4f7ff782011-01-15 05:29:12 +0000367 name="GNU Readline 6.1.2",
368 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
369 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000370 patchlevel='0',
371 patches=[
372 # The readline maintainers don't do actual micro releases, but
373 # just ship a set of patches.
Ned Deily4a96a372013-01-29 00:08:32 -0800374 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
375 'c642f2e84d820884b0bf9fd176bc6c3f'),
376 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
377 '1a76781a1ea734e831588285db7ec9b1'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000378 ]
379 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000380 ])
381
Ned Deily4f7ff782011-01-15 05:29:12 +0000382 if not PYTHON_3:
383 result.extend([
384 dict(
385 name="Sleepycat DB 4.7.25",
386 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
387 checksum='ec2b87e833779681a0c3a814aa71359e',
388 buildDir="build_unix",
389 configure="../dist/configure",
390 configure_pre=[
391 '--includedir=/usr/local/include/db4',
392 ]
393 ),
394 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000395
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000396 return result
397
Thomas Wouters477c8d52006-05-27 19:21:47 +0000398
Thomas Wouters477c8d52006-05-27 19:21:47 +0000399# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000400def pkg_recipes():
401 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
402 result = [
403 dict(
404 name="PythonFramework",
405 long_name="Python Framework",
406 source="/Library/Frameworks/Python.framework",
407 readme="""\
408 This package installs Python.framework, that is the python
409 interpreter and the standard library. This also includes Python
410 wrappers for lots of Mac OS X API's.
411 """,
412 postflight="scripts/postflight.framework",
413 selected='selected',
414 ),
415 dict(
416 name="PythonApplications",
417 long_name="GUI Applications",
418 source="/Applications/Python %(VER)s",
419 readme="""\
420 This package installs IDLE (an interactive Python IDE),
421 Python Launcher and Build Applet (create application bundles
422 from python scripts).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000423
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000424 It also installs a number of examples and demos.
425 """,
426 required=False,
427 selected='selected',
428 ),
429 dict(
430 name="PythonUnixTools",
431 long_name="UNIX command-line tools",
432 source="/usr/local/bin",
433 readme="""\
434 This package installs the unix tools in /usr/local/bin for
435 compatibility with older releases of Python. This package
436 is not necessary to use Python.
437 """,
438 required=False,
439 selected='selected',
440 ),
441 dict(
442 name="PythonDocumentation",
443 long_name="Python Documentation",
444 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
445 source="/pydocs",
446 readme="""\
447 This package installs the python documentation at a location
Ned Deily4a96a372013-01-29 00:08:32 -0800448 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000449 """,
450 postflight="scripts/postflight.documentation",
451 required=False,
452 selected='selected',
453 ),
454 dict(
455 name="PythonProfileChanges",
456 long_name="Shell profile updater",
457 readme="""\
458 This packages updates your shell profile to make sure that
459 the Python tools are found by your shell in preference of
460 the system provided Python tools.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000461
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000462 If you don't install this package you'll have to add
463 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
464 to your PATH by hand.
465 """,
466 postflight="scripts/postflight.patch-profile",
467 topdir="/Library/Frameworks/Python.framework",
468 source="/empty-dir",
469 required=False,
Ned Deilyed730102014-11-14 18:55:05 -0800470 selected='selected',
471 ),
472 dict(
473 name="PythonInstallPip",
474 long_name="Install or upgrade pip",
475 readme="""\
476 This package installs (or upgrades from an earlier version)
477 pip, a tool for installing and managing Python packages.
478 """,
479 postflight="scripts/postflight.ensurepip",
480 topdir="/Library/Frameworks/Python.framework",
481 source="/empty-dir",
482 required=False,
483 selected='selected',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000484 ),
485 ]
486
Ned Deily04cdfa12014-06-25 13:36:14 -0700487 if getDeptargetTuple() < (10, 4) and not PYTHON_3:
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000488 result.append(
489 dict(
490 name="PythonSystemFixes",
491 long_name="Fix system Python",
492 readme="""\
493 This package updates the system python installation on
494 Mac OS X 10.3 to ensure that you can build new python extensions
495 using that copy of python after installing this version.
496 """,
497 postflight="../Tools/fixapplepython23.py",
498 topdir="/Library/Frameworks/Python.framework",
499 source="/empty-dir",
500 required=False,
501 selected=unselected_for_python3,
502 )
503 )
Ned Deily41ab6c32013-11-22 22:25:43 -0800504
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000505 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000506
Thomas Wouters477c8d52006-05-27 19:21:47 +0000507def fatal(msg):
508 """
509 A fatal error, bail out.
510 """
511 sys.stderr.write('FATAL: ')
512 sys.stderr.write(msg)
513 sys.stderr.write('\n')
514 sys.exit(1)
515
516def fileContents(fn):
517 """
518 Return the contents of the named file
519 """
Ned Deily4a96a372013-01-29 00:08:32 -0800520 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000521
522def runCommand(commandline):
523 """
Ezio Melotti13925002011-03-16 11:05:33 +0200524 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000525 unless the command fails.
526 """
527 fd = os.popen(commandline, 'r')
528 data = fd.read()
529 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000530 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000531 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800532 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000533
534 if VERBOSE:
535 sys.stdout.write(data); sys.stdout.flush()
536
537def captureCommand(commandline):
538 fd = os.popen(commandline, 'r')
539 data = fd.read()
540 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000541 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000542 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800543 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000544
545 return data
546
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000547def getTclTkVersion(configfile, versionline):
548 """
549 search Tcl or Tk configuration file for version line
550 """
551 try:
552 f = open(configfile, "r")
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300553 except OSError:
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000554 fatal("Framework configuration file not found: %s" % configfile)
555
556 for l in f:
557 if l.startswith(versionline):
558 f.close()
559 return l
560
561 fatal("Version variable %s not found in framework configuration file: %s"
562 % (versionline, configfile))
563
Thomas Wouters477c8d52006-05-27 19:21:47 +0000564def checkEnvironment():
565 """
566 Check that we're running on a supported system.
567 """
568
Ned Deilye59e4c52011-01-29 18:56:28 +0000569 if sys.version_info[0:2] < (2, 4):
570 fatal("This script must be run with Python 2.4 or later")
571
Thomas Wouters477c8d52006-05-27 19:21:47 +0000572 if platform.system() != 'Darwin':
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000573 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000574
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000575 if int(platform.release().split('.')[0]) < 8:
576 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000577
578 if not os.path.exists(SDKPATH):
579 fatal("Please install the latest version of Xcode and the %s SDK"%(
580 os.path.basename(SDKPATH[:-4])))
581
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000582 # Because we only support dynamic load of only one major/minor version of
583 # Tcl/Tk, ensure:
584 # 1. there are no user-installed frameworks of Tcl/Tk with version
Ned Deily4a96a372013-01-29 00:08:32 -0800585 # higher than the Apple-supplied system version in
586 # SDKROOT/System/Library/Frameworks
587 # 2. there is a user-installed framework (usually ActiveTcl) in (or linked
588 # in) SDKROOT/Library/Frameworks with the same version as the system
589 # version. This allows users to choose to install a newer patch level.
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000590
Ned Deily4a96a372013-01-29 00:08:32 -0800591 frameworks = {}
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000592 for framework in ['Tcl', 'Tk']:
Ned Deily4a96a372013-01-29 00:08:32 -0800593 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000594 sysfw = os.path.join(SDKPATH, 'System', fwpth)
Ned Deily4a96a372013-01-29 00:08:32 -0800595 libfw = os.path.join(SDKPATH, fwpth)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000596 usrfw = os.path.join(os.getenv('HOME'), fwpth)
Ned Deily4a96a372013-01-29 00:08:32 -0800597 frameworks[framework] = os.readlink(sysfw)
598 if not os.path.exists(libfw):
599 fatal("Please install a link to a current %s %s as %s so "
600 "the user can override the system framework."
601 % (framework, frameworks[framework], libfw))
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000602 if os.readlink(libfw) != os.readlink(sysfw):
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000603 fatal("Version of %s must match %s" % (libfw, sysfw) )
604 if os.path.exists(usrfw):
605 fatal("Please rename %s to avoid possible dynamic load issues."
606 % usrfw)
607
Ned Deily4a96a372013-01-29 00:08:32 -0800608 if frameworks['Tcl'] != frameworks['Tk']:
609 fatal("The Tcl and Tk frameworks are not the same version.")
610
611 # add files to check after build
612 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
613 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
614 % frameworks['Tcl'],
615 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
616 % frameworks['Tk'],
617 ]
618
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000619 # Remove inherited environment variables which might influence build
620 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
621 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
622 for ev in list(os.environ):
623 for prefix in environ_var_prefixes:
624 if ev.startswith(prefix) :
Ned Deily4a96a372013-01-29 00:08:32 -0800625 print("INFO: deleting environment variable %s=%s" % (
626 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000627 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000628
Ned Deily4a96a372013-01-29 00:08:32 -0800629 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
630 if 'SDK_TOOLS_BIN' in os.environ:
631 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
632 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
633 # add its fixed location here if it exists
634 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
635 if os.path.isdir(OLD_DEVELOPER_TOOLS):
636 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
637 os.environ['PATH'] = base_path
638 print("Setting default PATH: %s"%(os.environ['PATH']))
Ned Deilyb364d9f2017-07-24 04:58:43 -0400639 # Ensure we have access to sphinx-build.
640 # You may have to create a link in /usr/bin for it.
Ned Deily1ff32a92014-09-05 15:57:05 -0700641 runCommand('sphinx-build --version')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000642
Thomas Wouters89f507f2006-12-13 04:49:30 +0000643def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000644 """
645 Parse arguments and update global settings.
646 """
Ronald Oussoren1943f862009-03-30 19:39:14 +0000647 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
Ned Deily4a96a372013-01-29 00:08:32 -0800648 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily5d3febf2014-12-13 00:17:46 -0800649 global FW_VERSION_PREFIX
Ned Deilydde4f632016-09-12 09:39:23 -0400650 global FW_SSL_DIRECTORY
Thomas Wouters477c8d52006-05-27 19:21:47 +0000651
652 if args is None:
653 args = sys.argv[1:]
654
655 try:
656 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000657 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
658 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily4a96a372013-01-29 00:08:32 -0800659 except getopt.GetoptError:
660 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000661 sys.exit(1)
662
663 if args:
Ned Deily4a96a372013-01-29 00:08:32 -0800664 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000665 sys.exit(1)
666
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000667 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000668 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000669 if k in ('-h', '-?', '--help'):
Ned Deily4a96a372013-01-29 00:08:32 -0800670 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000671 sys.exit(0)
672
673 elif k in ('-d', '--build-dir'):
674 WORKDIR=v
675
676 elif k in ('--third-party',):
677 DEPSRC=v
678
679 elif k in ('--sdk-path',):
680 SDKPATH=v
681
682 elif k in ('--src-dir',):
683 SRCDIR=v
684
Ronald Oussoren1943f862009-03-30 19:39:14 +0000685 elif k in ('--dep-target', ):
686 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000687 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000688
689 elif k in ('--universal-archs', ):
690 if v in UNIVERSALOPTS:
691 UNIVERSALARCHS = v
692 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000693 if deptarget is None:
694 # Select alternate default deployment
695 # target
696 DEPTARGET = default_target_map.get(v, '10.3')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000697 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800698 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000699
Thomas Wouters477c8d52006-05-27 19:21:47 +0000700 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800701 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000702
703 SRCDIR=os.path.abspath(SRCDIR)
704 WORKDIR=os.path.abspath(WORKDIR)
705 SDKPATH=os.path.abspath(SDKPATH)
706 DEPSRC=os.path.abspath(DEPSRC)
707
Ned Deily04cdfa12014-06-25 13:36:14 -0700708 CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000709
Ned Deily5d3febf2014-12-13 00:17:46 -0800710 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
Ned Deilydde4f632016-09-12 09:39:23 -0400711 FW_SSL_DIRECTORY = FW_VERSION_PREFIX[:] + ["etc", "openssl"]
Ned Deily5d3febf2014-12-13 00:17:46 -0800712
713 print("-- Settings:")
714 print(" * Source directory: %s" % SRCDIR)
715 print(" * Build directory: %s" % WORKDIR)
716 print(" * SDK location: %s" % SDKPATH)
717 print(" * Third-party source: %s" % DEPSRC)
718 print(" * Deployment target: %s" % DEPTARGET)
719 print(" * Universal archs: %s" % str(ARCHLIST))
720 print(" * C compiler: %s" % CC)
721 print(" * C++ compiler: %s" % CXX)
Ned Deily4a96a372013-01-29 00:08:32 -0800722 print("")
Ned Deily5d3febf2014-12-13 00:17:46 -0800723 print(" -- Building a Python %s framework at patch level %s"
724 % (getVersion(), getFullVersion()))
725 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000726
727def extractArchive(builddir, archiveName):
728 """
729 Extract a source archive into 'builddir'. Returns the path of the
730 extracted archive.
731
732 XXX: This function assumes that archives contain a toplevel directory
733 that is has the same name as the basename of the archive. This is
Ned Deily5b3582c2013-10-25 00:41:46 -0700734 safe enough for almost anything we use. Unfortunately, it does not
735 work for current Tcl and Tk source releases where the basename of
736 the archive ends with "-src" but the uncompressed directory does not.
737 For now, just special case Tcl and Tk tar.gz downloads.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000738 """
739 curdir = os.getcwd()
740 try:
741 os.chdir(builddir)
742 if archiveName.endswith('.tar.gz'):
743 retval = os.path.basename(archiveName[:-7])
Ned Deily5b3582c2013-10-25 00:41:46 -0700744 if ((retval.startswith('tcl') or retval.startswith('tk'))
745 and retval.endswith('-src')):
746 retval = retval[:-4]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000747 if os.path.exists(retval):
748 shutil.rmtree(retval)
749 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
750
751 elif archiveName.endswith('.tar.bz2'):
752 retval = os.path.basename(archiveName[:-8])
753 if os.path.exists(retval):
754 shutil.rmtree(retval)
755 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
756
757 elif archiveName.endswith('.tar'):
758 retval = os.path.basename(archiveName[:-4])
759 if os.path.exists(retval):
760 shutil.rmtree(retval)
761 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
762
763 elif archiveName.endswith('.zip'):
764 retval = os.path.basename(archiveName[:-4])
765 if os.path.exists(retval):
766 shutil.rmtree(retval)
767 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
768
769 data = fp.read()
770 xit = fp.close()
771 if xit is not None:
772 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800773 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000774
775 return os.path.join(builddir, retval)
776
777 finally:
778 os.chdir(curdir)
779
Thomas Wouters477c8d52006-05-27 19:21:47 +0000780def downloadURL(url, fname):
781 """
782 Download the contents of the url into the file.
783 """
Ned Deily4a96a372013-01-29 00:08:32 -0800784 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000785 fpOut = open(fname, 'wb')
786 block = fpIn.read(10240)
787 try:
788 while block:
789 fpOut.write(block)
790 block = fpIn.read(10240)
791 fpIn.close()
792 fpOut.close()
793 except:
794 try:
795 os.unlink(fname)
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300796 except OSError:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000797 pass
798
Ned Deily4a96a372013-01-29 00:08:32 -0800799def verifyThirdPartyFile(url, checksum, fname):
800 """
801 Download file from url to filename fname if it does not already exist.
802 Abort if file contents does not match supplied md5 checksum.
803 """
804 name = os.path.basename(fname)
805 if os.path.exists(fname):
806 print("Using local copy of %s"%(name,))
807 else:
808 print("Did not find local copy of %s"%(name,))
809 print("Downloading %s"%(name,))
810 downloadURL(url, fname)
811 print("Archive for %s stored as %s"%(name, fname))
812 if os.system(
813 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
814 % (shellQuote(fname), checksum) ):
815 fatal('MD5 checksum mismatch for file %s' % fname)
816
Ned Deily5d3febf2014-12-13 00:17:46 -0800817def build_universal_openssl(basedir, archList):
818 """
819 Special case build recipe for universal build of openssl.
820
821 The upstream OpenSSL build system does not directly support
822 OS X universal builds. We need to build each architecture
823 separately then lipo them together into fat libraries.
824 """
825
826 # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4).
827 # If we are building on a 10.4.x or earlier system,
828 # unilaterally disable assembly code building to avoid the problem.
829 no_asm = int(platform.release().split(".")[0]) < 9
830
831 def build_openssl_arch(archbase, arch):
832 "Build one architecture of openssl"
833 arch_opts = {
834 "i386": ["darwin-i386-cc"],
835 "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
836 "ppc": ["darwin-ppc-cc"],
837 "ppc64": ["darwin64-ppc-cc"],
838 }
839 configure_opts = [
840 "no-krb5",
841 "no-idea",
842 "no-mdc2",
843 "no-rc5",
844 "no-zlib",
845 "enable-tlsext",
846 "no-ssl2",
847 "no-ssl3",
Ned Deily5d3febf2014-12-13 00:17:46 -0800848 # "enable-unit-test",
849 "shared",
850 "--install_prefix=%s"%shellQuote(archbase),
851 "--prefix=%s"%os.path.join("/", *FW_VERSION_PREFIX),
Ned Deilydde4f632016-09-12 09:39:23 -0400852 "--openssldir=%s"%os.path.join("/", *FW_SSL_DIRECTORY),
Ned Deily5d3febf2014-12-13 00:17:46 -0800853 ]
854 if no_asm:
855 configure_opts.append("no-asm")
856 runCommand(" ".join(["perl", "Configure"]
857 + arch_opts[arch] + configure_opts))
858 runCommand("make depend OSX_SDK=%s" % SDKPATH)
859 runCommand("make all OSX_SDK=%s" % SDKPATH)
860 runCommand("make install_sw OSX_SDK=%s" % SDKPATH)
861 # runCommand("make test")
862 return
863
864 srcdir = os.getcwd()
865 universalbase = os.path.join(srcdir, "..",
866 os.path.basename(srcdir) + "-universal")
867 os.mkdir(universalbase)
868 archbasefws = []
869 for arch in archList:
870 # fresh copy of the source tree
871 archsrc = os.path.join(universalbase, arch, "src")
872 shutil.copytree(srcdir, archsrc, symlinks=True)
873 # install base for this arch
874 archbase = os.path.join(universalbase, arch, "root")
875 os.mkdir(archbase)
876 # Python framework base within install_prefix:
877 # the build will install into this framework..
878 # This is to ensure that the resulting shared libs have
879 # the desired real install paths built into them.
880 archbasefw = os.path.join(archbase, *FW_VERSION_PREFIX)
881
882 # build one architecture
883 os.chdir(archsrc)
884 build_openssl_arch(archbase, arch)
885 os.chdir(srcdir)
886 archbasefws.append(archbasefw)
887
888 # copy arch-independent files from last build into the basedir framework
889 basefw = os.path.join(basedir, *FW_VERSION_PREFIX)
890 shutil.copytree(
891 os.path.join(archbasefw, "include", "openssl"),
892 os.path.join(basefw, "include", "openssl")
893 )
894
895 shlib_version_number = grepValue(os.path.join(archsrc, "Makefile"),
896 "SHLIB_VERSION_NUMBER")
897 # e.g. -> "1.0.0"
898 libcrypto = "libcrypto.dylib"
899 libcrypto_versioned = libcrypto.replace(".", "."+shlib_version_number+".")
900 # e.g. -> "libcrypto.1.0.0.dylib"
901 libssl = "libssl.dylib"
902 libssl_versioned = libssl.replace(".", "."+shlib_version_number+".")
903 # e.g. -> "libssl.1.0.0.dylib"
904
905 try:
906 os.mkdir(os.path.join(basefw, "lib"))
907 except OSError:
908 pass
909
910 # merge the individual arch-dependent shared libs into a fat shared lib
911 archbasefws.insert(0, basefw)
912 for (lib_unversioned, lib_versioned) in [
913 (libcrypto, libcrypto_versioned),
914 (libssl, libssl_versioned)
915 ]:
916 runCommand("lipo -create -output " +
917 " ".join(shellQuote(
918 os.path.join(fw, "lib", lib_versioned))
919 for fw in archbasefws))
920 # and create an unversioned symlink of it
921 os.symlink(lib_versioned, os.path.join(basefw, "lib", lib_unversioned))
922
923 # Create links in the temp include and lib dirs that will be injected
924 # into the Python build so that setup.py can find them while building
925 # and the versioned links so that the setup.py post-build import test
926 # does not fail.
927 relative_path = os.path.join("..", "..", "..", *FW_VERSION_PREFIX)
928 for fn in [
929 ["include", "openssl"],
930 ["lib", libcrypto],
931 ["lib", libssl],
932 ["lib", libcrypto_versioned],
933 ["lib", libssl_versioned],
934 ]:
935 os.symlink(
936 os.path.join(relative_path, *fn),
937 os.path.join(basedir, "usr", "local", *fn)
938 )
939
940 return
941
Thomas Wouters477c8d52006-05-27 19:21:47 +0000942def buildRecipe(recipe, basedir, archList):
943 """
944 Build software using a recipe. This function does the
945 'configure;make;make install' dance for C software, with a possibility
946 to customize this process, basically a poor-mans DarwinPorts.
947 """
948 curdir = os.getcwd()
949
950 name = recipe['name']
Ned Deily5d3febf2014-12-13 00:17:46 -0800951 THIRD_PARTY_LIBS.append(name)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000952 url = recipe['url']
953 configure = recipe.get('configure', './configure')
Ned Deily5d3febf2014-12-13 00:17:46 -0800954 buildrecipe = recipe.get('buildrecipe', None)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000955 install = recipe.get('install', 'make && make install DESTDIR=%s'%(
956 shellQuote(basedir)))
957
958 archiveName = os.path.split(url)[-1]
959 sourceArchive = os.path.join(DEPSRC, archiveName)
960
961 if not os.path.exists(DEPSRC):
962 os.mkdir(DEPSRC)
963
Ned Deily4a96a372013-01-29 00:08:32 -0800964 verifyThirdPartyFile(url, recipe['checksum'], sourceArchive)
965 print("Extracting archive for %s"%(name,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000966 buildDir=os.path.join(WORKDIR, '_bld')
967 if not os.path.exists(buildDir):
968 os.mkdir(buildDir)
969
970 workDir = extractArchive(buildDir, sourceArchive)
971 os.chdir(workDir)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000972
Ned Deily4a96a372013-01-29 00:08:32 -0800973 for patch in recipe.get('patches', ()):
974 if isinstance(patch, tuple):
975 url, checksum = patch
976 fn = os.path.join(DEPSRC, os.path.basename(url))
977 verifyThirdPartyFile(url, checksum, fn)
978 else:
979 # patch is a file in the source directory
980 fn = os.path.join(curdir, patch)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000981 runCommand('patch -p%s < %s'%(recipe.get('patchlevel', 1),
982 shellQuote(fn),))
983
Ned Deily4a96a372013-01-29 00:08:32 -0800984 for patchscript in recipe.get('patchscripts', ()):
985 if isinstance(patchscript, tuple):
986 url, checksum = patchscript
987 fn = os.path.join(DEPSRC, os.path.basename(url))
988 verifyThirdPartyFile(url, checksum, fn)
989 else:
990 # patch is a file in the source directory
991 fn = os.path.join(curdir, patchscript)
992 if fn.endswith('.bz2'):
993 runCommand('bunzip2 -fk %s' % shellQuote(fn))
994 fn = fn[:-4]
995 runCommand('sh %s' % shellQuote(fn))
996 os.unlink(fn)
997
Ned Deily94764b22013-10-27 19:49:29 -0700998 if 'buildDir' in recipe:
999 os.chdir(recipe['buildDir'])
1000
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001001 if configure is not None:
1002 configure_args = [
1003 "--prefix=/usr/local",
1004 "--enable-static",
1005 "--disable-shared",
1006 #"CPP=gcc -arch %s -E"%(' -arch '.join(archList,),),
1007 ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001008
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001009 if 'configure_pre' in recipe:
1010 args = list(recipe['configure_pre'])
1011 if '--disable-static' in args:
1012 configure_args.remove('--enable-static')
1013 if '--enable-shared' in args:
1014 configure_args.remove('--disable-shared')
1015 configure_args.extend(args)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001016
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001017 if recipe.get('useLDFlags', 1):
1018 configure_args.extend([
Ned Deily4a96a372013-01-29 00:08:32 -08001019 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
1020 "-I%s/usr/local/include"%(
1021 recipe.get('extra_cflags', ''),
1022 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001023 ' -arch '.join(archList),
1024 shellQuote(SDKPATH)[1:-1],
1025 shellQuote(basedir)[1:-1],),
Ned Deily0134a352014-04-09 16:16:08 -07001026 "LDFLAGS=-mmacosx-version-min=%s -isysroot %s -L%s/usr/local/lib -arch %s"%(
Ned Deily4a96a372013-01-29 00:08:32 -08001027 DEPTARGET,
Thomas Wouters477c8d52006-05-27 19:21:47 +00001028 shellQuote(SDKPATH)[1:-1],
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001029 shellQuote(basedir)[1:-1],
1030 ' -arch '.join(archList)),
1031 ])
1032 else:
1033 configure_args.extend([
Ned Deily4a96a372013-01-29 00:08:32 -08001034 "CFLAGS=%s-mmacosx-version-min=%s -arch %s -isysroot %s "
1035 "-I%s/usr/local/include"%(
1036 recipe.get('extra_cflags', ''),
1037 DEPTARGET,
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001038 ' -arch '.join(archList),
1039 shellQuote(SDKPATH)[1:-1],
1040 shellQuote(basedir)[1:-1],),
1041 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001042
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001043 if 'configure_post' in recipe:
Ned Deily4a96a372013-01-29 00:08:32 -08001044 configure_args = configure_args + list(recipe['configure_post'])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001045
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001046 configure_args.insert(0, configure)
1047 configure_args = [ shellQuote(a) for a in configure_args ]
Thomas Wouters477c8d52006-05-27 19:21:47 +00001048
Ned Deily4a96a372013-01-29 00:08:32 -08001049 print("Running configure for %s"%(name,))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001050 runCommand(' '.join(configure_args) + ' 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001051
Ned Deily5d3febf2014-12-13 00:17:46 -08001052 if buildrecipe is not None:
1053 # call special-case build recipe, e.g. for openssl
1054 buildrecipe(basedir, archList)
1055
1056 if install is not None:
1057 print("Running install for %s"%(name,))
1058 runCommand('{ ' + install + ' ;} 2>&1')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001059
Ned Deily4a96a372013-01-29 00:08:32 -08001060 print("Done %s"%(name,))
1061 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001062
1063 os.chdir(curdir)
1064
1065def buildLibraries():
1066 """
1067 Build our dependencies into $WORKDIR/libraries/usr/local
1068 """
Ned Deily4a96a372013-01-29 00:08:32 -08001069 print("")
1070 print("Building required libraries")
1071 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001072 universal = os.path.join(WORKDIR, 'libraries')
1073 os.mkdir(universal)
1074 os.makedirs(os.path.join(universal, 'usr', 'local', 'lib'))
1075 os.makedirs(os.path.join(universal, 'usr', 'local', 'include'))
1076
Ronald Oussoren1943f862009-03-30 19:39:14 +00001077 for recipe in library_recipes():
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001078 buildRecipe(recipe, universal, ARCHLIST)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001079
1080
1081
1082def buildPythonDocs():
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001083 # This stores the documentation as Resources/English.lproj/Documentation
Mike53f7a7c2017-12-14 14:04:53 +03001084 # inside the framework. pydoc and IDLE will pick it up there.
Ned Deily4a96a372013-01-29 00:08:32 -08001085 print("Install python documentation")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001086 rootDir = os.path.join(WORKDIR, '_root')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001087 buildDir = os.path.join('../../Doc')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001088 docdir = os.path.join(rootDir, 'pydocs')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001089 curDir = os.getcwd()
1090 os.chdir(buildDir)
Ned Deily1ff32a92014-09-05 15:57:05 -07001091 runCommand('make clean')
Ned Deily4c7532e2017-07-23 16:39:54 -04001092 # Create virtual environment for docs builds with blurb and sphinx
1093 runCommand('make venv')
1094 runCommand('make html PYTHON=venv/bin/python')
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001095 os.chdir(curDir)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001096 if not os.path.exists(docdir):
1097 os.mkdir(docdir)
Ronald Oussorenf84d7e92009-05-19 11:27:25 +00001098 os.rename(os.path.join(buildDir, 'build', 'html'), docdir)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001099
1100
1101def buildPython():
Ned Deily4a96a372013-01-29 00:08:32 -08001102 print("Building a universal python for %s architectures" % UNIVERSALARCHS)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001103
1104 buildDir = os.path.join(WORKDIR, '_bld', 'python')
1105 rootDir = os.path.join(WORKDIR, '_root')
1106
1107 if os.path.exists(buildDir):
1108 shutil.rmtree(buildDir)
1109 if os.path.exists(rootDir):
1110 shutil.rmtree(rootDir)
Ned Deily4f7ff782011-01-15 05:29:12 +00001111 os.makedirs(buildDir)
1112 os.makedirs(rootDir)
1113 os.makedirs(os.path.join(rootDir, 'empty-dir'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001114 curdir = os.getcwd()
1115 os.chdir(buildDir)
1116
1117 # Not sure if this is still needed, the original build script
1118 # claims that parts of the install assume python.exe exists.
1119 os.symlink('python', os.path.join(buildDir, 'python.exe'))
1120
1121 # Extract the version from the configure file, needed to calculate
1122 # several paths.
1123 version = getVersion()
1124
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001125 # Since the extra libs are not in their installed framework location
1126 # during the build, augment the library path so that the interpreter
1127 # will find them during its extension import sanity checks.
1128 os.environ['DYLD_LIBRARY_PATH'] = os.path.join(WORKDIR,
1129 'libraries', 'usr', 'local', 'lib')
Ned Deily4a96a372013-01-29 00:08:32 -08001130 print("Running configure...")
Ronald Oussoren1943f862009-03-30 19:39:14 +00001131 runCommand("%s -C --enable-framework --enable-universalsdk=%s "
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001132 "--with-universal-archs=%s "
1133 "%s "
Ned Deily41ab6c32013-11-22 22:25:43 -08001134 "%s "
Ronald Oussoren1943f862009-03-30 19:39:14 +00001135 "LDFLAGS='-g -L%s/libraries/usr/local/lib' "
Ned Deily4b7a0232013-10-25 00:46:02 -07001136 "CFLAGS='-g -I%s/libraries/usr/local/include' 2>&1"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001137 shellQuote(os.path.join(SRCDIR, 'configure')), shellQuote(SDKPATH),
1138 UNIVERSALARCHS,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001139 (' ', '--with-computed-gotos ')[PYTHON_3],
Ned Deilyed730102014-11-14 18:55:05 -08001140 (' ', '--without-ensurepip ')[PYTHON_3],
Ronald Oussoren1943f862009-03-30 19:39:14 +00001141 shellQuote(WORKDIR)[1:-1],
Thomas Wouters477c8d52006-05-27 19:21:47 +00001142 shellQuote(WORKDIR)[1:-1]))
1143
Ned Deilyb364d9f2017-07-24 04:58:43 -04001144 # Look for environment value BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS
1145 # and, if defined, append its value to the make command. This allows
1146 # us to pass in version control tags, like GITTAG, to a build from a
1147 # tarball rather than from a vcs checkout, thus eliminating the need
1148 # to have a working copy of the vcs program on the build machine.
1149 #
1150 # A typical use might be:
1151 # export BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS=" \
1152 # GITVERSION='echo 123456789a' \
1153 # GITTAG='echo v3.6.0' \
1154 # GITBRANCH='echo 3.6'"
1155
1156 make_extras = os.getenv("BUILDINSTALLER_BUILDPYTHON_MAKE_EXTRAS")
1157 if make_extras:
1158 make_cmd = "make " + make_extras
1159 else:
1160 make_cmd = "make"
1161 print("Running " + make_cmd)
1162 runCommand(make_cmd)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001163
Ned Deily4a96a372013-01-29 00:08:32 -08001164 print("Running make install")
Ronald Oussorenf84d7e92009-05-19 11:27:25 +00001165 runCommand("make install DESTDIR=%s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001166 shellQuote(rootDir)))
1167
Ned Deily4a96a372013-01-29 00:08:32 -08001168 print("Running make frameworkinstallextras")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001169 runCommand("make frameworkinstallextras DESTDIR=%s"%(
1170 shellQuote(rootDir)))
1171
Ronald Oussorenac4b39f2009-03-30 20:05:35 +00001172 del os.environ['DYLD_LIBRARY_PATH']
Ned Deily4a96a372013-01-29 00:08:32 -08001173 print("Copying required shared libraries")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001174 if os.path.exists(os.path.join(WORKDIR, 'libraries', 'Library')):
1175 runCommand("mv %s/* %s"%(
1176 shellQuote(os.path.join(
1177 WORKDIR, 'libraries', 'Library', 'Frameworks',
1178 'Python.framework', 'Versions', getVersion(),
1179 'lib')),
1180 shellQuote(os.path.join(WORKDIR, '_root', 'Library', 'Frameworks',
1181 'Python.framework', 'Versions', getVersion(),
1182 'lib'))))
1183
Ned Deilydde4f632016-09-12 09:39:23 -04001184 frmDir = os.path.join(rootDir, 'Library', 'Frameworks', 'Python.framework')
1185 frmDirVersioned = os.path.join(frmDir, 'Versions', version)
1186 path_to_lib = os.path.join(frmDirVersioned, 'lib', 'python%s'%(version,))
1187 # create directory for OpenSSL certificates
1188 sslDir = os.path.join(frmDirVersioned, 'etc', 'openssl')
1189 os.makedirs(sslDir)
Ned Deily050fcd52013-10-26 03:16:44 -07001190
Ned Deily4a96a372013-01-29 00:08:32 -08001191 print("Fix file modes")
Thomas Wouters89f507f2006-12-13 04:49:30 +00001192 gid = grp.getgrnam('admin').gr_gid
1193
Ned Deily4a96a372013-01-29 00:08:32 -08001194 shared_lib_error = False
Thomas Wouters477c8d52006-05-27 19:21:47 +00001195 for dirpath, dirnames, filenames in os.walk(frmDir):
1196 for dn in dirnames:
Ned Deily4a96a372013-01-29 00:08:32 -08001197 os.chmod(os.path.join(dirpath, dn), STAT_0o775)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001198 os.chown(os.path.join(dirpath, dn), -1, gid)
1199
Thomas Wouters477c8d52006-05-27 19:21:47 +00001200 for fn in filenames:
1201 if os.path.islink(fn):
1202 continue
1203
1204 # "chmod g+w $fn"
1205 p = os.path.join(dirpath, fn)
1206 st = os.stat(p)
Thomas Wouters89f507f2006-12-13 04:49:30 +00001207 os.chmod(p, stat.S_IMODE(st.st_mode) | stat.S_IWGRP)
1208 os.chown(p, -1, gid)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001209
Ned Deily4a96a372013-01-29 00:08:32 -08001210 if fn in EXPECTED_SHARED_LIBS:
1211 # check to see that this file was linked with the
1212 # expected library path and version
1213 data = captureCommand("otool -L %s" % shellQuote(p))
1214 for sl in EXPECTED_SHARED_LIBS[fn]:
1215 if ("\t%s " % sl) not in data:
1216 print("Expected shared lib %s was not linked with %s"
1217 % (sl, p))
1218 shared_lib_error = True
1219
1220 if shared_lib_error:
1221 fatal("Unexpected shared library errors.")
1222
Ned Deilye59e4c52011-01-29 18:56:28 +00001223 if PYTHON_3:
1224 LDVERSION=None
1225 VERSION=None
1226 ABIFLAGS=None
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001227
Ned Deilye59e4c52011-01-29 18:56:28 +00001228 fp = open(os.path.join(buildDir, 'Makefile'), 'r')
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001229 for ln in fp:
1230 if ln.startswith('VERSION='):
1231 VERSION=ln.split()[1]
1232 if ln.startswith('ABIFLAGS='):
1233 ABIFLAGS=ln.split()[1]
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001234 if ln.startswith('LDVERSION='):
1235 LDVERSION=ln.split()[1]
Ned Deilye59e4c52011-01-29 18:56:28 +00001236 fp.close()
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001237
Ned Deilye59e4c52011-01-29 18:56:28 +00001238 LDVERSION = LDVERSION.replace('$(VERSION)', VERSION)
1239 LDVERSION = LDVERSION.replace('$(ABIFLAGS)', ABIFLAGS)
1240 config_suffix = '-' + LDVERSION
Ned Deily652bad42016-08-15 14:37:14 -04001241 if getVersionMajorMinor() >= (3, 6):
1242 config_suffix = config_suffix + '-darwin'
Ned Deilye59e4c52011-01-29 18:56:28 +00001243 else:
1244 config_suffix = '' # Python 2.x
Ronald Oussoren0499d0b2010-12-07 14:41:05 +00001245
Thomas Wouters477c8d52006-05-27 19:21:47 +00001246 # We added some directories to the search path during the configure
1247 # phase. Remove those because those directories won't be there on
Ned Deily4a96a372013-01-29 00:08:32 -08001248 # the end-users system. Also remove the directories from _sysconfigdata.py
1249 # (added in 3.3) if it exists.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001250
Ned Deilya4f6b002013-10-25 00:47:38 -07001251 include_path = '-I%s/libraries/usr/local/include' % (WORKDIR,)
1252 lib_path = '-L%s/libraries/usr/local/lib' % (WORKDIR,)
1253
Ned Deilya4f6b002013-10-25 00:47:38 -07001254 # fix Makefile
1255 path = os.path.join(path_to_lib, 'config' + config_suffix, 'Makefile')
1256 fp = open(path, 'r')
1257 data = fp.read()
1258 fp.close()
1259
1260 for p in (include_path, lib_path):
1261 data = data.replace(" " + p, '')
1262 data = data.replace(p + " ", '')
1263
1264 fp = open(path, 'w')
1265 fp.write(data)
1266 fp.close()
1267
Ned Deily652bad42016-08-15 14:37:14 -04001268 # fix _sysconfigdata
Ned Deilya4f6b002013-10-25 00:47:38 -07001269 #
1270 # TODO: make this more robust! test_sysconfig_module of
1271 # distutils.tests.test_sysconfig.SysconfigTestCase tests that
1272 # the output from get_config_var in both sysconfig and
1273 # distutils.sysconfig is exactly the same for both CFLAGS and
1274 # LDFLAGS. The fixing up is now complicated by the pretty
1275 # printing in _sysconfigdata.py. Also, we are using the
1276 # pprint from the Python running the installer build which
1277 # may not cosmetically format the same as the pprint in the Python
1278 # being built (and which is used to originally generate
1279 # _sysconfigdata.py).
1280
1281 import pprint
Ned Deily652bad42016-08-15 14:37:14 -04001282 if getVersionMajorMinor() >= (3, 6):
Zachary Warec4b53af2016-09-09 17:59:49 -07001283 # XXX this is extra-fragile
1284 path = os.path.join(path_to_lib, '_sysconfigdata_m_darwin_darwin.py')
Ned Deily652bad42016-08-15 14:37:14 -04001285 else:
1286 path = os.path.join(path_to_lib, '_sysconfigdata.py')
1287 fp = open(path, 'r')
1288 data = fp.read()
1289 fp.close()
1290 # create build_time_vars dict
1291 exec(data)
1292 vars = {}
1293 for k, v in build_time_vars.items():
1294 if type(v) == type(''):
1295 for p in (include_path, lib_path):
1296 v = v.replace(' ' + p, '')
1297 v = v.replace(p + ' ', '')
1298 vars[k] = v
Ned Deily4a96a372013-01-29 00:08:32 -08001299
Ned Deily652bad42016-08-15 14:37:14 -04001300 fp = open(path, 'w')
1301 # duplicated from sysconfig._generate_posix_vars()
1302 fp.write('# system configuration generated and used by'
1303 ' the sysconfig module\n')
1304 fp.write('build_time_vars = ')
1305 pprint.pprint(vars, stream=fp)
1306 fp.close()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001307
1308 # Add symlinks in /usr/local/bin, using relative links
1309 usr_local_bin = os.path.join(rootDir, 'usr', 'local', 'bin')
1310 to_framework = os.path.join('..', '..', '..', 'Library', 'Frameworks',
1311 'Python.framework', 'Versions', version, 'bin')
1312 if os.path.exists(usr_local_bin):
1313 shutil.rmtree(usr_local_bin)
1314 os.makedirs(usr_local_bin)
1315 for fn in os.listdir(
1316 os.path.join(frmDir, 'Versions', version, 'bin')):
1317 os.symlink(os.path.join(to_framework, fn),
1318 os.path.join(usr_local_bin, fn))
1319
1320 os.chdir(curdir)
1321
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001322 if PYTHON_3:
Ezio Melotti7c4a7e62013-08-26 01:32:56 +03001323 # Remove the 'Current' link, that way we don't accidentally mess
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001324 # with an already installed version of python 2
1325 os.unlink(os.path.join(rootDir, 'Library', 'Frameworks',
1326 'Python.framework', 'Versions', 'Current'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001327
1328def patchFile(inPath, outPath):
1329 data = fileContents(inPath)
1330 data = data.replace('$FULL_VERSION', getFullVersion())
1331 data = data.replace('$VERSION', getVersion())
Ronald Oussoren1943f862009-03-30 19:39:14 +00001332 data = data.replace('$MACOSX_DEPLOYMENT_TARGET', ''.join((DEPTARGET, ' or later')))
Ronald Oussorend0103292010-10-20 12:56:56 +00001333 data = data.replace('$ARCHITECTURES', ", ".join(universal_opts_map[UNIVERSALARCHS]))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001334 data = data.replace('$INSTALL_SIZE', installSize())
Ned Deily5d3febf2014-12-13 00:17:46 -08001335 data = data.replace('$THIRD_PARTY_LIBS', "\\\n".join(THIRD_PARTY_LIBS))
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001336
1337 # This one is not handy as a template variable
1338 data = data.replace('$PYTHONFRAMEWORKINSTALLDIR', '/Library/Frameworks/Python.framework')
Ned Deily4a96a372013-01-29 00:08:32 -08001339 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001340 fp.write(data)
1341 fp.close()
1342
1343def patchScript(inPath, outPath):
Ned Deilyed730102014-11-14 18:55:05 -08001344 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001345 data = fileContents(inPath)
Ned Deilyed730102014-11-14 18:55:05 -08001346 data = data.replace('@PYMAJOR@', str(major))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001347 data = data.replace('@PYVER@', getVersion())
Ned Deily4a96a372013-01-29 00:08:32 -08001348 fp = open(outPath, 'w')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001349 fp.write(data)
1350 fp.close()
Ned Deily4a96a372013-01-29 00:08:32 -08001351 os.chmod(outPath, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001352
1353
1354
1355def packageFromRecipe(targetDir, recipe):
1356 curdir = os.getcwd()
1357 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +00001358 # The major version (such as 2.5) is included in the package name
1359 # because having two version of python installed at the same time is
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001360 # common.
1361 pkgname = '%s-%s'%(recipe['name'], getVersion())
Thomas Wouters477c8d52006-05-27 19:21:47 +00001362 srcdir = recipe.get('source')
1363 pkgroot = recipe.get('topdir', srcdir)
1364 postflight = recipe.get('postflight')
1365 readme = textwrap.dedent(recipe['readme'])
1366 isRequired = recipe.get('required', True)
1367
Ned Deily4a96a372013-01-29 00:08:32 -08001368 print("- building package %s"%(pkgname,))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001369
1370 # Substitute some variables
1371 textvars = dict(
1372 VER=getVersion(),
1373 FULLVER=getFullVersion(),
1374 )
1375 readme = readme % textvars
1376
1377 if pkgroot is not None:
1378 pkgroot = pkgroot % textvars
1379 else:
1380 pkgroot = '/'
1381
1382 if srcdir is not None:
1383 srcdir = os.path.join(WORKDIR, '_root', srcdir[1:])
1384 srcdir = srcdir % textvars
1385
1386 if postflight is not None:
1387 postflight = os.path.abspath(postflight)
1388
1389 packageContents = os.path.join(targetDir, pkgname + '.pkg', 'Contents')
1390 os.makedirs(packageContents)
1391
1392 if srcdir is not None:
1393 os.chdir(srcdir)
1394 runCommand("pax -wf %s . 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1395 runCommand("gzip -9 %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.pax')),))
1396 runCommand("mkbom . %s 2>&1"%(shellQuote(os.path.join(packageContents, 'Archive.bom')),))
1397
1398 fn = os.path.join(packageContents, 'PkgInfo')
1399 fp = open(fn, 'w')
1400 fp.write('pmkrpkg1')
1401 fp.close()
1402
1403 rsrcDir = os.path.join(packageContents, "Resources")
1404 os.mkdir(rsrcDir)
1405 fp = open(os.path.join(rsrcDir, 'ReadMe.txt'), 'w')
1406 fp.write(readme)
1407 fp.close()
1408
1409 if postflight is not None:
1410 patchScript(postflight, os.path.join(rsrcDir, 'postflight'))
1411
1412 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001413 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001414 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001415 CFBundleGetInfoString="Python.%s %s"%(pkgname, vers,),
1416 CFBundleIdentifier='org.python.Python.%s'%(pkgname,),
1417 CFBundleName='Python.%s'%(pkgname,),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001418 CFBundleShortVersionString=vers,
1419 IFMajorVersion=major,
1420 IFMinorVersion=minor,
1421 IFPkgFormatVersion=0.10000000149011612,
1422 IFPkgFlagAllowBackRev=False,
1423 IFPkgFlagAuthorizationAction="RootAuthorization",
1424 IFPkgFlagDefaultLocation=pkgroot,
1425 IFPkgFlagFollowLinks=True,
1426 IFPkgFlagInstallFat=True,
1427 IFPkgFlagIsRequired=isRequired,
1428 IFPkgFlagOverwritePermissions=False,
1429 IFPkgFlagRelocatable=False,
1430 IFPkgFlagRestartAction="NoRestart",
1431 IFPkgFlagRootVolumeOnly=True,
1432 IFPkgFlagUpdateInstalledLangauges=False,
1433 )
1434 writePlist(pl, os.path.join(packageContents, 'Info.plist'))
1435
1436 pl = Plist(
1437 IFPkgDescriptionDescription=readme,
Ronald Oussoren1943f862009-03-30 19:39:14 +00001438 IFPkgDescriptionTitle=recipe.get('long_name', "Python.%s"%(pkgname,)),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001439 IFPkgDescriptionVersion=vers,
1440 )
1441 writePlist(pl, os.path.join(packageContents, 'Resources', 'Description.plist'))
1442
1443 finally:
1444 os.chdir(curdir)
1445
1446
1447def makeMpkgPlist(path):
1448
1449 vers = getFullVersion()
Ned Deily4a96a372013-01-29 00:08:32 -08001450 major, minor = getVersionMajorMinor()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001451
1452 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001453 CFBundleGetInfoString="Python %s"%(vers,),
1454 CFBundleIdentifier='org.python.Python',
1455 CFBundleName='Python',
Thomas Wouters477c8d52006-05-27 19:21:47 +00001456 CFBundleShortVersionString=vers,
1457 IFMajorVersion=major,
1458 IFMinorVersion=minor,
1459 IFPkgFlagComponentDirectory="Contents/Packages",
1460 IFPkgFlagPackageList=[
1461 dict(
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001462 IFPkgFlagPackageLocation='%s-%s.pkg'%(item['name'], getVersion()),
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001463 IFPkgFlagPackageSelection=item.get('selected', 'selected'),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001464 )
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001465 for item in pkg_recipes()
Thomas Wouters477c8d52006-05-27 19:21:47 +00001466 ],
1467 IFPkgFormatVersion=0.10000000149011612,
1468 IFPkgFlagBackgroundScaling="proportional",
1469 IFPkgFlagBackgroundAlignment="left",
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001470 IFPkgFlagAuthorizationAction="RootAuthorization",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001471 )
1472
1473 writePlist(pl, path)
1474
1475
1476def buildInstaller():
1477
1478 # Zap all compiled files
1479 for dirpath, _, filenames in os.walk(os.path.join(WORKDIR, '_root')):
1480 for fn in filenames:
1481 if fn.endswith('.pyc') or fn.endswith('.pyo'):
1482 os.unlink(os.path.join(dirpath, fn))
1483
1484 outdir = os.path.join(WORKDIR, 'installer')
1485 if os.path.exists(outdir):
1486 shutil.rmtree(outdir)
1487 os.mkdir(outdir)
1488
Ronald Oussoren1943f862009-03-30 19:39:14 +00001489 pkgroot = os.path.join(outdir, 'Python.mpkg', 'Contents')
Thomas Wouters477c8d52006-05-27 19:21:47 +00001490 pkgcontents = os.path.join(pkgroot, 'Packages')
1491 os.makedirs(pkgcontents)
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001492 for recipe in pkg_recipes():
Thomas Wouters477c8d52006-05-27 19:21:47 +00001493 packageFromRecipe(pkgcontents, recipe)
1494
1495 rsrcDir = os.path.join(pkgroot, 'Resources')
1496
1497 fn = os.path.join(pkgroot, 'PkgInfo')
1498 fp = open(fn, 'w')
1499 fp.write('pmkrpkg1')
1500 fp.close()
1501
1502 os.mkdir(rsrcDir)
1503
1504 makeMpkgPlist(os.path.join(pkgroot, 'Info.plist'))
1505 pl = Plist(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001506 IFPkgDescriptionTitle="Python",
Thomas Wouters477c8d52006-05-27 19:21:47 +00001507 IFPkgDescriptionVersion=getVersion(),
1508 )
1509
1510 writePlist(pl, os.path.join(pkgroot, 'Resources', 'Description.plist'))
1511 for fn in os.listdir('resources'):
1512 if fn == '.svn': continue
1513 if fn.endswith('.jpg'):
1514 shutil.copy(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1515 else:
1516 patchFile(os.path.join('resources', fn), os.path.join(rsrcDir, fn))
1517
Thomas Wouters477c8d52006-05-27 19:21:47 +00001518
1519def installSize(clear=False, _saved=[]):
1520 if clear:
1521 del _saved[:]
1522 if not _saved:
1523 data = captureCommand("du -ks %s"%(
1524 shellQuote(os.path.join(WORKDIR, '_root'))))
1525 _saved.append("%d"%((0.5 + (int(data.split()[0]) / 1024.0)),))
1526 return _saved[0]
1527
1528
1529def buildDMG():
1530 """
Thomas Wouters89f507f2006-12-13 04:49:30 +00001531 Create DMG containing the rootDir.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001532 """
1533 outdir = os.path.join(WORKDIR, 'diskimage')
1534 if os.path.exists(outdir):
1535 shutil.rmtree(outdir)
1536
1537 imagepath = os.path.join(outdir,
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001538 'python-%s-macosx%s'%(getFullVersion(),DEPTARGET))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001539 if INCLUDE_TIMESTAMP:
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001540 imagepath = imagepath + '-%04d-%02d-%02d'%(time.localtime()[:3])
Thomas Wouters477c8d52006-05-27 19:21:47 +00001541 imagepath = imagepath + '.dmg'
1542
1543 os.mkdir(outdir)
Ronald Oussoren1943f862009-03-30 19:39:14 +00001544 volname='Python %s'%(getFullVersion())
1545 runCommand("hdiutil create -format UDRW -volname %s -srcfolder %s %s"%(
1546 shellQuote(volname),
Thomas Wouters477c8d52006-05-27 19:21:47 +00001547 shellQuote(os.path.join(WORKDIR, 'installer')),
Ronald Oussoren1943f862009-03-30 19:39:14 +00001548 shellQuote(imagepath + ".tmp.dmg" )))
1549
1550
1551 if not os.path.exists(os.path.join(WORKDIR, "mnt")):
1552 os.mkdir(os.path.join(WORKDIR, "mnt"))
1553 runCommand("hdiutil attach %s -mountroot %s"%(
1554 shellQuote(imagepath + ".tmp.dmg"), shellQuote(os.path.join(WORKDIR, "mnt"))))
1555
1556 # Custom icon for the DMG, shown when the DMG is mounted.
1557 shutil.copy("../Icons/Disk Image.icns",
1558 os.path.join(WORKDIR, "mnt", volname, ".VolumeIcon.icns"))
Ned Deily4a96a372013-01-29 00:08:32 -08001559 runCommand("SetFile -a C %s/"%(
Ronald Oussoren1943f862009-03-30 19:39:14 +00001560 shellQuote(os.path.join(WORKDIR, "mnt", volname)),))
1561
1562 runCommand("hdiutil detach %s"%(shellQuote(os.path.join(WORKDIR, "mnt", volname))))
1563
1564 setIcon(imagepath + ".tmp.dmg", "../Icons/Disk Image.icns")
1565 runCommand("hdiutil convert %s -format UDZO -o %s"%(
1566 shellQuote(imagepath + ".tmp.dmg"), shellQuote(imagepath)))
1567 setIcon(imagepath, "../Icons/Disk Image.icns")
1568
1569 os.unlink(imagepath + ".tmp.dmg")
Thomas Wouters477c8d52006-05-27 19:21:47 +00001570
1571 return imagepath
1572
1573
1574def setIcon(filePath, icnsPath):
1575 """
1576 Set the custom icon for the specified file or directory.
Thomas Wouters477c8d52006-05-27 19:21:47 +00001577 """
Thomas Wouters477c8d52006-05-27 19:21:47 +00001578
Ronald Oussoren70050672010-04-30 15:00:26 +00001579 dirPath = os.path.normpath(os.path.dirname(__file__))
1580 toolPath = os.path.join(dirPath, "seticon.app/Contents/MacOS/seticon")
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001581 if not os.path.exists(toolPath) or os.stat(toolPath).st_mtime < os.stat(dirPath + '/seticon.m').st_mtime:
1582 # NOTE: The tool is created inside an .app bundle, otherwise it won't work due
1583 # to connections to the window server.
Ronald Oussoren70050672010-04-30 15:00:26 +00001584 appPath = os.path.join(dirPath, "seticon.app/Contents/MacOS")
1585 if not os.path.exists(appPath):
1586 os.makedirs(appPath)
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001587 runCommand("cc -o %s %s/seticon.m -framework Cocoa"%(
1588 shellQuote(toolPath), shellQuote(dirPath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001589
Ronald Oussoren207b4c22009-03-30 17:20:30 +00001590 runCommand("%s %s %s"%(shellQuote(os.path.abspath(toolPath)), shellQuote(icnsPath),
1591 shellQuote(filePath)))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001592
1593def main():
1594 # First parse options and check if we can perform our work
1595 parseOptions()
1596 checkEnvironment()
1597
Ronald Oussoren1943f862009-03-30 19:39:14 +00001598 os.environ['MACOSX_DEPLOYMENT_TARGET'] = DEPTARGET
Benjamin Petersond9b7d482010-03-19 21:42:45 +00001599 os.environ['CC'] = CC
Ned Deily4a96a372013-01-29 00:08:32 -08001600 os.environ['CXX'] = CXX
Thomas Wouters477c8d52006-05-27 19:21:47 +00001601
1602 if os.path.exists(WORKDIR):
1603 shutil.rmtree(WORKDIR)
1604 os.mkdir(WORKDIR)
1605
Ronald Oussorenc45c3d92010-04-18 15:24:17 +00001606 os.environ['LC_ALL'] = 'C'
1607
Thomas Wouters477c8d52006-05-27 19:21:47 +00001608 # Then build third-party libraries such as sleepycat DB4.
1609 buildLibraries()
1610
1611 # Now build python itself
1612 buildPython()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001613
1614 # And then build the documentation
1615 # Remove the Deployment Target from the shell
1616 # environment, it's no longer needed and
1617 # an unexpected build target can cause problems
1618 # when Sphinx and its dependencies need to
1619 # be (re-)installed.
1620 del os.environ['MACOSX_DEPLOYMENT_TARGET']
Thomas Wouters477c8d52006-05-27 19:21:47 +00001621 buildPythonDocs()
Ronald Oussoren6bf63672009-03-31 13:25:17 +00001622
1623
1624 # Prepare the applications folder
Benjamin Petersonf10a79a2008-10-11 00:49:57 +00001625 folder = os.path.join(WORKDIR, "_root", "Applications", "Python %s"%(
Thomas Wouters477c8d52006-05-27 19:21:47 +00001626 getVersion(),))
Ned Deily5d3febf2014-12-13 00:17:46 -08001627 fn = os.path.join(folder, "License.rtf")
1628 patchFile("resources/License.rtf", fn)
1629 fn = os.path.join(folder, "ReadMe.rtf")
1630 patchFile("resources/ReadMe.rtf", fn)
1631 fn = os.path.join(folder, "Update Shell Profile.command")
1632 patchScript("scripts/postflight.patch-profile", fn)
Ned Deilydde4f632016-09-12 09:39:23 -04001633 fn = os.path.join(folder, "Install Certificates.command")
1634 patchScript("resources/install_certificates.command", fn)
Ned Deily4a96a372013-01-29 00:08:32 -08001635 os.chmod(folder, STAT_0o755)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001636 setIcon(folder, "../Icons/Python Folder.icns")
1637
1638 # Create the installer
1639 buildInstaller()
1640
1641 # And copy the readme into the directory containing the installer
Ned Deily5d3febf2014-12-13 00:17:46 -08001642 patchFile('resources/ReadMe.rtf',
1643 os.path.join(WORKDIR, 'installer', 'ReadMe.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001644
1645 # Ditto for the license file.
Ned Deily5d3febf2014-12-13 00:17:46 -08001646 patchFile('resources/License.rtf',
1647 os.path.join(WORKDIR, 'installer', 'License.rtf'))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001648
1649 fp = open(os.path.join(WORKDIR, 'installer', 'Build.txt'), 'w')
Ned Deily4a96a372013-01-29 00:08:32 -08001650 fp.write("# BUILD INFO\n")
1651 fp.write("# Date: %s\n" % time.ctime())
1652 fp.write("# By: %s\n" % pwd.getpwuid(os.getuid()).pw_gecos)
Thomas Wouters477c8d52006-05-27 19:21:47 +00001653 fp.close()
1654
Thomas Wouters477c8d52006-05-27 19:21:47 +00001655 # And copy it to a DMG
1656 buildDMG()
1657
Thomas Wouters477c8d52006-05-27 19:21:47 +00001658if __name__ == "__main__":
1659 main()