blob: 4cb7e3214cd40a0c7abbf6f3ffaa2bfdce39d1bb [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 Deilycfcd7672017-03-04 02:33:25 -0500216 name="OpenSSL 1.0.2k",
217 url="https://www.openssl.org/source/openssl-1.0.2k.tar.gz",
218 checksum='f965fc0bf01bf882b31314b61391ae65',
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 Deilyb3b07672016-09-05 17:31:14 -0700273 name="XZ 5.2.2",
274 url="http://tukaani.org/xz/xz-5.2.2.tar.gz",
275 checksum='7cf6a8544a7dae8e8106fdf7addfa28c',
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(
Ned Deily473ce1d2016-10-31 19:32:48 -0400318 name="SQLite 3.14.2",
319 url="https://www.sqlite.org/2016/sqlite-autoconf-3140200.tar.gz",
320 checksum='90c53cacb811db27f990b8292bd96159',
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 '
325 '-DSQLITE_ENABLE_RTREE '
326 '-DSQLITE_TCL=0 '
327 '%s' % ('','-DSQLITE_WITHOUT_ZONEMALLOC ')[LT_10_5]),
328 configure_pre=[
329 '--enable-threadsafe',
330 '--enable-shared=no',
331 '--enable-static=yes',
332 '--disable-readline',
333 '--disable-dependency-tracking',
334 ]
335 ),
336 ])
337
Ned Deily04cdfa12014-06-25 13:36:14 -0700338 if getDeptargetTuple() < (10, 5):
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000339 result.extend([
340 dict(
Ned Deily4f7ff782011-01-15 05:29:12 +0000341 name="Bzip2 1.0.6",
342 url="http://bzip.org/1.0.6/bzip2-1.0.6.tar.gz",
343 checksum='00b516f4704d4a7cb50a1d97e6e8e15b',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000344 configure=None,
Ned Deily4a96a372013-01-29 00:08:32 -0800345 install='make install CC=%s CXX=%s, PREFIX=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
346 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000347 shellQuote(os.path.join(WORKDIR, 'libraries')),
348 ' -arch '.join(ARCHLIST),
349 SDKPATH,
350 ),
351 ),
352 dict(
353 name="ZLib 1.2.3",
354 url="http://www.gzip.org/zlib/zlib-1.2.3.tar.gz",
355 checksum='debc62758716a169df9f62e6ab2bc634',
356 configure=None,
Ned Deily4a96a372013-01-29 00:08:32 -0800357 install='make install CC=%s CXX=%s, prefix=%s/usr/local/ CFLAGS="-arch %s -isysroot %s"'%(
358 CC, CXX,
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000359 shellQuote(os.path.join(WORKDIR, 'libraries')),
360 ' -arch '.join(ARCHLIST),
361 SDKPATH,
362 ),
363 ),
364 dict(
365 # Note that GNU readline is GPL'd software
Ned Deily4f7ff782011-01-15 05:29:12 +0000366 name="GNU Readline 6.1.2",
367 url="http://ftp.gnu.org/pub/gnu/readline/readline-6.1.tar.gz" ,
368 checksum='fc2f7e714fe792db1ce6ddc4c9fb4ef3',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000369 patchlevel='0',
370 patches=[
371 # The readline maintainers don't do actual micro releases, but
372 # just ship a set of patches.
Ned Deily4a96a372013-01-29 00:08:32 -0800373 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-001',
374 'c642f2e84d820884b0bf9fd176bc6c3f'),
375 ('http://ftp.gnu.org/pub/gnu/readline/readline-6.1-patches/readline61-002',
376 '1a76781a1ea734e831588285db7ec9b1'),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000377 ]
378 ),
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000379 ])
380
Ned Deily4f7ff782011-01-15 05:29:12 +0000381 if not PYTHON_3:
382 result.extend([
383 dict(
384 name="Sleepycat DB 4.7.25",
385 url="http://download.oracle.com/berkeley-db/db-4.7.25.tar.gz",
386 checksum='ec2b87e833779681a0c3a814aa71359e',
387 buildDir="build_unix",
388 configure="../dist/configure",
389 configure_pre=[
390 '--includedir=/usr/local/include/db4',
391 ]
392 ),
393 ])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000394
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000395 return result
396
Thomas Wouters477c8d52006-05-27 19:21:47 +0000397
Thomas Wouters477c8d52006-05-27 19:21:47 +0000398# Instructions for building packages inside the .mpkg.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000399def pkg_recipes():
400 unselected_for_python3 = ('selected', 'unselected')[PYTHON_3]
401 result = [
402 dict(
403 name="PythonFramework",
404 long_name="Python Framework",
405 source="/Library/Frameworks/Python.framework",
406 readme="""\
407 This package installs Python.framework, that is the python
408 interpreter and the standard library. This also includes Python
409 wrappers for lots of Mac OS X API's.
410 """,
411 postflight="scripts/postflight.framework",
412 selected='selected',
413 ),
414 dict(
415 name="PythonApplications",
416 long_name="GUI Applications",
417 source="/Applications/Python %(VER)s",
418 readme="""\
419 This package installs IDLE (an interactive Python IDE),
420 Python Launcher and Build Applet (create application bundles
421 from python scripts).
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000422
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000423 It also installs a number of examples and demos.
424 """,
425 required=False,
426 selected='selected',
427 ),
428 dict(
429 name="PythonUnixTools",
430 long_name="UNIX command-line tools",
431 source="/usr/local/bin",
432 readme="""\
433 This package installs the unix tools in /usr/local/bin for
434 compatibility with older releases of Python. This package
435 is not necessary to use Python.
436 """,
437 required=False,
438 selected='selected',
439 ),
440 dict(
441 name="PythonDocumentation",
442 long_name="Python Documentation",
443 topdir="/Library/Frameworks/Python.framework/Versions/%(VER)s/Resources/English.lproj/Documentation",
444 source="/pydocs",
445 readme="""\
446 This package installs the python documentation at a location
Ned Deily4a96a372013-01-29 00:08:32 -0800447 that is useable for pydoc and IDLE.
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000448 """,
449 postflight="scripts/postflight.documentation",
450 required=False,
451 selected='selected',
452 ),
453 dict(
454 name="PythonProfileChanges",
455 long_name="Shell profile updater",
456 readme="""\
457 This packages updates your shell profile to make sure that
458 the Python tools are found by your shell in preference of
459 the system provided Python tools.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000460
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000461 If you don't install this package you'll have to add
462 "/Library/Frameworks/Python.framework/Versions/%(VER)s/bin"
463 to your PATH by hand.
464 """,
465 postflight="scripts/postflight.patch-profile",
466 topdir="/Library/Frameworks/Python.framework",
467 source="/empty-dir",
468 required=False,
Ned Deilyed730102014-11-14 18:55:05 -0800469 selected='selected',
470 ),
471 dict(
472 name="PythonInstallPip",
473 long_name="Install or upgrade pip",
474 readme="""\
475 This package installs (or upgrades from an earlier version)
476 pip, a tool for installing and managing Python packages.
477 """,
478 postflight="scripts/postflight.ensurepip",
479 topdir="/Library/Frameworks/Python.framework",
480 source="/empty-dir",
481 required=False,
482 selected='selected',
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000483 ),
484 ]
485
Ned Deily04cdfa12014-06-25 13:36:14 -0700486 if getDeptargetTuple() < (10, 4) and not PYTHON_3:
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000487 result.append(
488 dict(
489 name="PythonSystemFixes",
490 long_name="Fix system Python",
491 readme="""\
492 This package updates the system python installation on
493 Mac OS X 10.3 to ensure that you can build new python extensions
494 using that copy of python after installing this version.
495 """,
496 postflight="../Tools/fixapplepython23.py",
497 topdir="/Library/Frameworks/Python.framework",
498 source="/empty-dir",
499 required=False,
500 selected=unselected_for_python3,
501 )
502 )
Ned Deily41ab6c32013-11-22 22:25:43 -0800503
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000504 return result
Thomas Wouters477c8d52006-05-27 19:21:47 +0000505
Thomas Wouters477c8d52006-05-27 19:21:47 +0000506def fatal(msg):
507 """
508 A fatal error, bail out.
509 """
510 sys.stderr.write('FATAL: ')
511 sys.stderr.write(msg)
512 sys.stderr.write('\n')
513 sys.exit(1)
514
515def fileContents(fn):
516 """
517 Return the contents of the named file
518 """
Ned Deily4a96a372013-01-29 00:08:32 -0800519 return open(fn, 'r').read()
Thomas Wouters477c8d52006-05-27 19:21:47 +0000520
521def runCommand(commandline):
522 """
Ezio Melotti13925002011-03-16 11:05:33 +0200523 Run a command and raise RuntimeError if it fails. Output is suppressed
Thomas Wouters477c8d52006-05-27 19:21:47 +0000524 unless the command fails.
525 """
526 fd = os.popen(commandline, 'r')
527 data = fd.read()
528 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000529 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000530 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800531 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000532
533 if VERBOSE:
534 sys.stdout.write(data); sys.stdout.flush()
535
536def captureCommand(commandline):
537 fd = os.popen(commandline, 'r')
538 data = fd.read()
539 xit = fd.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000540 if xit is not None:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000541 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800542 raise RuntimeError("command failed: %s"%(commandline,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000543
544 return data
545
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000546def getTclTkVersion(configfile, versionline):
547 """
548 search Tcl or Tk configuration file for version line
549 """
550 try:
551 f = open(configfile, "r")
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300552 except OSError:
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000553 fatal("Framework configuration file not found: %s" % configfile)
554
555 for l in f:
556 if l.startswith(versionline):
557 f.close()
558 return l
559
560 fatal("Version variable %s not found in framework configuration file: %s"
561 % (versionline, configfile))
562
Thomas Wouters477c8d52006-05-27 19:21:47 +0000563def checkEnvironment():
564 """
565 Check that we're running on a supported system.
566 """
567
Ned Deilye59e4c52011-01-29 18:56:28 +0000568 if sys.version_info[0:2] < (2, 4):
569 fatal("This script must be run with Python 2.4 or later")
570
Thomas Wouters477c8d52006-05-27 19:21:47 +0000571 if platform.system() != 'Darwin':
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000572 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000573
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000574 if int(platform.release().split('.')[0]) < 8:
575 fatal("This script should be run on a Mac OS X 10.4 (or later) system")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000576
577 if not os.path.exists(SDKPATH):
578 fatal("Please install the latest version of Xcode and the %s SDK"%(
579 os.path.basename(SDKPATH[:-4])))
580
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000581 # Because we only support dynamic load of only one major/minor version of
582 # Tcl/Tk, ensure:
583 # 1. there are no user-installed frameworks of Tcl/Tk with version
Ned Deily4a96a372013-01-29 00:08:32 -0800584 # higher than the Apple-supplied system version in
585 # SDKROOT/System/Library/Frameworks
586 # 2. there is a user-installed framework (usually ActiveTcl) in (or linked
587 # in) SDKROOT/Library/Frameworks with the same version as the system
588 # version. This allows users to choose to install a newer patch level.
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000589
Ned Deily4a96a372013-01-29 00:08:32 -0800590 frameworks = {}
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000591 for framework in ['Tcl', 'Tk']:
Ned Deily4a96a372013-01-29 00:08:32 -0800592 fwpth = 'Library/Frameworks/%s.framework/Versions/Current' % framework
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000593 sysfw = os.path.join(SDKPATH, 'System', fwpth)
Ned Deily4a96a372013-01-29 00:08:32 -0800594 libfw = os.path.join(SDKPATH, fwpth)
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000595 usrfw = os.path.join(os.getenv('HOME'), fwpth)
Ned Deily4a96a372013-01-29 00:08:32 -0800596 frameworks[framework] = os.readlink(sysfw)
597 if not os.path.exists(libfw):
598 fatal("Please install a link to a current %s %s as %s so "
599 "the user can override the system framework."
600 % (framework, frameworks[framework], libfw))
Ronald Oussoren0499d0b2010-12-07 14:41:05 +0000601 if os.readlink(libfw) != os.readlink(sysfw):
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000602 fatal("Version of %s must match %s" % (libfw, sysfw) )
603 if os.path.exists(usrfw):
604 fatal("Please rename %s to avoid possible dynamic load issues."
605 % usrfw)
606
Ned Deily4a96a372013-01-29 00:08:32 -0800607 if frameworks['Tcl'] != frameworks['Tk']:
608 fatal("The Tcl and Tk frameworks are not the same version.")
609
610 # add files to check after build
611 EXPECTED_SHARED_LIBS['_tkinter.so'] = [
612 "/Library/Frameworks/Tcl.framework/Versions/%s/Tcl"
613 % frameworks['Tcl'],
614 "/Library/Frameworks/Tk.framework/Versions/%s/Tk"
615 % frameworks['Tk'],
616 ]
617
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000618 # Remove inherited environment variables which might influence build
619 environ_var_prefixes = ['CPATH', 'C_INCLUDE_', 'DYLD_', 'LANG', 'LC_',
620 'LD_', 'LIBRARY_', 'PATH', 'PYTHON']
621 for ev in list(os.environ):
622 for prefix in environ_var_prefixes:
623 if ev.startswith(prefix) :
Ned Deily4a96a372013-01-29 00:08:32 -0800624 print("INFO: deleting environment variable %s=%s" % (
625 ev, os.environ[ev]))
Ronald Oussorenc45c3d92010-04-18 15:24:17 +0000626 del os.environ[ev]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000627
Ned Deily4a96a372013-01-29 00:08:32 -0800628 base_path = '/bin:/sbin:/usr/bin:/usr/sbin'
629 if 'SDK_TOOLS_BIN' in os.environ:
630 base_path = os.environ['SDK_TOOLS_BIN'] + ':' + base_path
631 # Xcode 2.5 on OS X 10.4 does not include SetFile in its usr/bin;
632 # add its fixed location here if it exists
633 OLD_DEVELOPER_TOOLS = '/Developer/Tools'
634 if os.path.isdir(OLD_DEVELOPER_TOOLS):
635 base_path = base_path + ':' + OLD_DEVELOPER_TOOLS
636 os.environ['PATH'] = base_path
637 print("Setting default PATH: %s"%(os.environ['PATH']))
Ned Deilyb364d9f2017-07-24 04:58:43 -0400638 # Ensure we have access to sphinx-build.
639 # You may have to create a link in /usr/bin for it.
Ned Deily1ff32a92014-09-05 15:57:05 -0700640 runCommand('sphinx-build --version')
Thomas Wouters477c8d52006-05-27 19:21:47 +0000641
Thomas Wouters89f507f2006-12-13 04:49:30 +0000642def parseOptions(args=None):
Thomas Wouters477c8d52006-05-27 19:21:47 +0000643 """
644 Parse arguments and update global settings.
645 """
Ronald Oussoren1943f862009-03-30 19:39:14 +0000646 global WORKDIR, DEPSRC, SDKPATH, SRCDIR, DEPTARGET
Ned Deily4a96a372013-01-29 00:08:32 -0800647 global UNIVERSALOPTS, UNIVERSALARCHS, ARCHLIST, CC, CXX
Ned Deily5d3febf2014-12-13 00:17:46 -0800648 global FW_VERSION_PREFIX
Ned Deilydde4f632016-09-12 09:39:23 -0400649 global FW_SSL_DIRECTORY
Thomas Wouters477c8d52006-05-27 19:21:47 +0000650
651 if args is None:
652 args = sys.argv[1:]
653
654 try:
655 options, args = getopt.getopt(args, '?hb',
Ronald Oussoren1943f862009-03-30 19:39:14 +0000656 [ 'build-dir=', 'third-party=', 'sdk-path=' , 'src-dir=',
657 'dep-target=', 'universal-archs=', 'help' ])
Ned Deily4a96a372013-01-29 00:08:32 -0800658 except getopt.GetoptError:
659 print(sys.exc_info()[1])
Thomas Wouters477c8d52006-05-27 19:21:47 +0000660 sys.exit(1)
661
662 if args:
Ned Deily4a96a372013-01-29 00:08:32 -0800663 print("Additional arguments")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000664 sys.exit(1)
665
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000666 deptarget = None
Thomas Wouters477c8d52006-05-27 19:21:47 +0000667 for k, v in options:
Ronald Oussoren1943f862009-03-30 19:39:14 +0000668 if k in ('-h', '-?', '--help'):
Ned Deily4a96a372013-01-29 00:08:32 -0800669 print(USAGE)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000670 sys.exit(0)
671
672 elif k in ('-d', '--build-dir'):
673 WORKDIR=v
674
675 elif k in ('--third-party',):
676 DEPSRC=v
677
678 elif k in ('--sdk-path',):
679 SDKPATH=v
680
681 elif k in ('--src-dir',):
682 SRCDIR=v
683
Ronald Oussoren1943f862009-03-30 19:39:14 +0000684 elif k in ('--dep-target', ):
685 DEPTARGET=v
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000686 deptarget=v
Ronald Oussoren1943f862009-03-30 19:39:14 +0000687
688 elif k in ('--universal-archs', ):
689 if v in UNIVERSALOPTS:
690 UNIVERSALARCHS = v
691 ARCHLIST = universal_opts_map[UNIVERSALARCHS]
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000692 if deptarget is None:
693 # Select alternate default deployment
694 # target
695 DEPTARGET = default_target_map.get(v, '10.3')
Ronald Oussoren1943f862009-03-30 19:39:14 +0000696 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800697 raise NotImplementedError(v)
Ronald Oussoren1943f862009-03-30 19:39:14 +0000698
Thomas Wouters477c8d52006-05-27 19:21:47 +0000699 else:
Ned Deily4a96a372013-01-29 00:08:32 -0800700 raise NotImplementedError(k)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000701
702 SRCDIR=os.path.abspath(SRCDIR)
703 WORKDIR=os.path.abspath(WORKDIR)
704 SDKPATH=os.path.abspath(SDKPATH)
705 DEPSRC=os.path.abspath(DEPSRC)
706
Ned Deily04cdfa12014-06-25 13:36:14 -0700707 CC, CXX = getTargetCompilers()
Benjamin Petersond9b7d482010-03-19 21:42:45 +0000708
Ned Deily5d3febf2014-12-13 00:17:46 -0800709 FW_VERSION_PREFIX = FW_PREFIX[:] + ["Versions", getVersion()]
Ned Deilydde4f632016-09-12 09:39:23 -0400710 FW_SSL_DIRECTORY = FW_VERSION_PREFIX[:] + ["etc", "openssl"]
Ned Deily5d3febf2014-12-13 00:17:46 -0800711
712 print("-- Settings:")
713 print(" * Source directory: %s" % SRCDIR)
714 print(" * Build directory: %s" % WORKDIR)
715 print(" * SDK location: %s" % SDKPATH)
716 print(" * Third-party source: %s" % DEPSRC)
717 print(" * Deployment target: %s" % DEPTARGET)
718 print(" * Universal archs: %s" % str(ARCHLIST))
719 print(" * C compiler: %s" % CC)
720 print(" * C++ compiler: %s" % CXX)
Ned Deily4a96a372013-01-29 00:08:32 -0800721 print("")
Ned Deily5d3febf2014-12-13 00:17:46 -0800722 print(" -- Building a Python %s framework at patch level %s"
723 % (getVersion(), getFullVersion()))
724 print("")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000725
726def extractArchive(builddir, archiveName):
727 """
728 Extract a source archive into 'builddir'. Returns the path of the
729 extracted archive.
730
731 XXX: This function assumes that archives contain a toplevel directory
732 that is has the same name as the basename of the archive. This is
Ned Deily5b3582c2013-10-25 00:41:46 -0700733 safe enough for almost anything we use. Unfortunately, it does not
734 work for current Tcl and Tk source releases where the basename of
735 the archive ends with "-src" but the uncompressed directory does not.
736 For now, just special case Tcl and Tk tar.gz downloads.
Thomas Wouters477c8d52006-05-27 19:21:47 +0000737 """
738 curdir = os.getcwd()
739 try:
740 os.chdir(builddir)
741 if archiveName.endswith('.tar.gz'):
742 retval = os.path.basename(archiveName[:-7])
Ned Deily5b3582c2013-10-25 00:41:46 -0700743 if ((retval.startswith('tcl') or retval.startswith('tk'))
744 and retval.endswith('-src')):
745 retval = retval[:-4]
Thomas Wouters477c8d52006-05-27 19:21:47 +0000746 if os.path.exists(retval):
747 shutil.rmtree(retval)
748 fp = os.popen("tar zxf %s 2>&1"%(shellQuote(archiveName),), 'r')
749
750 elif archiveName.endswith('.tar.bz2'):
751 retval = os.path.basename(archiveName[:-8])
752 if os.path.exists(retval):
753 shutil.rmtree(retval)
754 fp = os.popen("tar jxf %s 2>&1"%(shellQuote(archiveName),), 'r')
755
756 elif archiveName.endswith('.tar'):
757 retval = os.path.basename(archiveName[:-4])
758 if os.path.exists(retval):
759 shutil.rmtree(retval)
760 fp = os.popen("tar xf %s 2>&1"%(shellQuote(archiveName),), 'r')
761
762 elif archiveName.endswith('.zip'):
763 retval = os.path.basename(archiveName[:-4])
764 if os.path.exists(retval):
765 shutil.rmtree(retval)
766 fp = os.popen("unzip %s 2>&1"%(shellQuote(archiveName),), 'r')
767
768 data = fp.read()
769 xit = fp.close()
770 if xit is not None:
771 sys.stdout.write(data)
Ned Deily4a96a372013-01-29 00:08:32 -0800772 raise RuntimeError("Cannot extract %s"%(archiveName,))
Thomas Wouters477c8d52006-05-27 19:21:47 +0000773
774 return os.path.join(builddir, retval)
775
776 finally:
777 os.chdir(curdir)
778
Thomas Wouters477c8d52006-05-27 19:21:47 +0000779def downloadURL(url, fname):
780 """
781 Download the contents of the url into the file.
782 """
Ned Deily4a96a372013-01-29 00:08:32 -0800783 fpIn = urllib_request.urlopen(url)
Thomas Wouters477c8d52006-05-27 19:21:47 +0000784 fpOut = open(fname, 'wb')
785 block = fpIn.read(10240)
786 try:
787 while block:
788 fpOut.write(block)
789 block = fpIn.read(10240)
790 fpIn.close()
791 fpOut.close()
792 except:
793 try:
794 os.unlink(fname)
Serhiy Storchakaba9ac5b2015-05-20 10:33:40 +0300795 except OSError:
Thomas Wouters477c8d52006-05-27 19:21:47 +0000796 pass
797
Ned Deily4a96a372013-01-29 00:08:32 -0800798def verifyThirdPartyFile(url, checksum, fname):
799 """
800 Download file from url to filename fname if it does not already exist.
801 Abort if file contents does not match supplied md5 checksum.
802 """
803 name = os.path.basename(fname)
804 if os.path.exists(fname):
805 print("Using local copy of %s"%(name,))
806 else:
807 print("Did not find local copy of %s"%(name,))
808 print("Downloading %s"%(name,))
809 downloadURL(url, fname)
810 print("Archive for %s stored as %s"%(name, fname))
811 if os.system(
812 'MD5=$(openssl md5 %s) ; test "${MD5##*= }" = "%s"'
813 % (shellQuote(fname), checksum) ):
814 fatal('MD5 checksum mismatch for file %s' % fname)
815
Ned Deily5d3febf2014-12-13 00:17:46 -0800816def build_universal_openssl(basedir, archList):
817 """
818 Special case build recipe for universal build of openssl.
819
820 The upstream OpenSSL build system does not directly support
821 OS X universal builds. We need to build each architecture
822 separately then lipo them together into fat libraries.
823 """
824
825 # OpenSSL fails to build with Xcode 2.5 (on OS X 10.4).
826 # If we are building on a 10.4.x or earlier system,
827 # unilaterally disable assembly code building to avoid the problem.
828 no_asm = int(platform.release().split(".")[0]) < 9
829
830 def build_openssl_arch(archbase, arch):
831 "Build one architecture of openssl"
832 arch_opts = {
833 "i386": ["darwin-i386-cc"],
834 "x86_64": ["darwin64-x86_64-cc", "enable-ec_nistp_64_gcc_128"],
835 "ppc": ["darwin-ppc-cc"],
836 "ppc64": ["darwin64-ppc-cc"],
837 }
838 configure_opts = [
839 "no-krb5",
840 "no-idea",
841 "no-mdc2",
842 "no-rc5",
843 "no-zlib",
844 "enable-tlsext",
845 "no-ssl2",
846 "no-ssl3",
847 "no-ssl3-method",
848 # "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
Thomas Wouters477c8d52006-05-27 19:21:47 +00001084 # inside the framwork. 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()