Explicitly specify the Mac SDK for wxPython builds
[wxWidgets.git] / build / tools / build-wxwidgets.py
1 #!/usr/bin/env python
2
3 ###################################
4 # Author: Kevin Ollivier
5 # Licence: wxWindows licence
6 ###################################
7
8 import os
9 import re
10 import sys
11 import builder
12 import commands
13 import glob
14 import optparse
15 import platform
16 import shutil
17 import types
18
19 # builder object
20 wxBuilder = None
21
22 # other globals
23 scriptDir = None
24 wxRootDir = None
25 contribDir = None
26 options = None
27 configure_opts = None
28 exitWithException = True
29
30
31 def exitIfError(code, msg):
32 if code != 0:
33 print msg
34 if exitWithException:
35 raise builder.BuildError, msg
36 else:
37 sys.exit(1)
38
39
40 def getWxRelease():
41 global wxRootDir
42 configureText = open(os.path.join(wxRootDir, "configure.in"), "r").read()
43
44 majorVersion = re.search("wx_major_version_number=(\d+)", configureText).group(1)
45 minorVersion = re.search("wx_minor_version_number=(\d+)", configureText).group(1)
46
47 return "%s.%s" % (majorVersion, minorVersion)
48
49
50
51 def doMacLipoBuild(arch, buildDir, installDir,
52 cxxcompiler="g++-4.0", cccompiler="gcc-4.0", target="10.4", flags=""):
53 archInstallDir = installDir + "/" + arch
54 old_env = dict(CXX = os.environ.get('CXX'),
55 CC = os.environ.get('CC'),
56 MACOSX_DEPLOYMENT_TARGET = os.environ.get('MACOSX_DEPLOYMENT_TARGET'),
57 )
58
59 os.environ["CXX"] = "%s -arch %s %s" % (cxxcompiler, arch, flags)
60 os.environ["CC"] = "%s -arch %s %s" % (cccompiler, arch, flags)
61 os.environ["MACOSX_DEPLOYMENT_TARGET"] = target
62 archArgs = ["DESTDIR=" + archInstallDir]
63 buildRoot = "bld-" + arch
64 if buildDir:
65 buildRoot = buildDir + "/" + buildRoot
66
67 if not os.path.exists(buildRoot):
68 os.makedirs(buildRoot)
69
70 olddir = os.getcwd()
71 os.chdir(buildRoot)
72
73 if not options.no_config:
74 exitIfError(wxBuilder.configure(dir=wxRootDir, options=configure_opts), "Error running configure for "+arch)
75 exitIfError(wxBuilder.build(options=archArgs), "Error building for "+arch)
76 exitIfError(wxBuilder.install(options=["DESTDIR=" + archInstallDir]), "Error Installing for "+arch)
77
78 if options.wxpython and os.path.exists(os.path.join(wxRootDir, contribDir)):
79 exitIfError(wxBuilder.build(dir=os.path.join(contribDir, "gizmos"), options=archArgs),
80 "Error building gizmos for "+arch)
81 exitIfError(wxBuilder.install(os.path.join(contribDir, "gizmos"), options=["DESTDIR=" + archInstallDir]),
82 "Error Installing gizmos for "+arch)
83
84 exitIfError(wxBuilder.build(dir=os.path.join(contribDir, "stc"),options=archArgs),
85 "Error building stc for "+arch)
86 exitIfError(wxBuilder.install(os.path.join(contribDir, "stc"),options=["DESTDIR=" + archInstallDir]),
87 "Error installing stc for "+arch)
88
89 os.chdir(olddir)
90 for key, val in old_env.items():
91 if val:
92 os.environ[key] = val
93 else:
94 del os.environ[key]
95
96
97 def macFixupInstallNames(destdir, prefix, buildDir=None):
98 # When an installdir is used then the install_names embedded in
99 # the dylibs are not correct. Reset the IDs and the dependencies
100 # to use just the prefix.
101 print "**** macFixupInstallNames(%s, %s, %s)" % (destdir, prefix, buildDir)
102 pwd = os.getcwd()
103 os.chdir(destdir+prefix+'/lib')
104 dylibs = glob.glob('*.dylib') # ('*[0-9].[0-9].[0-9].[0-9]*.dylib')
105 for lib in dylibs:
106 cmd = 'install_name_tool -id %s/lib/%s %s/lib/%s' % \
107 (prefix,lib, destdir+prefix,lib)
108 print cmd
109 os.system(cmd)
110 for dep in dylibs:
111 if buildDir is not None:
112 cmd = 'install_name_tool -change %s/lib/%s %s/lib/%s %s/lib/%s' % \
113 (buildDir,dep, prefix,dep, destdir+prefix,lib)
114 else:
115 cmd = 'install_name_tool -change %s/lib/%s %s/lib/%s %s/lib/%s' % \
116 (destdir+prefix,dep, prefix,dep, destdir+prefix,lib)
117 print cmd
118 os.system(cmd)
119 os.chdir(pwd)
120
121
122
123 def main(scriptName, args):
124 global scriptDir
125 global wxRootDir
126 global contribDir
127 global options
128 global configure_opts
129 global wxBuilder
130
131 scriptDir = os.path.dirname(os.path.abspath(scriptName))
132 wxRootDir = os.path.abspath(os.path.join(scriptDir, "..", ".."))
133
134 contribDir = os.path.join("contrib", "src")
135 installDir = None
136
137 VERSION = tuple([int(i) for i in getWxRelease().split('.')])
138
139 if sys.platform.startswith("win"):
140 contribDir = os.path.join(wxRootDir, "contrib", "build")
141
142 if sys.platform.startswith("win"):
143 toolkit = "msvc"
144 else:
145 toolkit = "autoconf"
146
147 option_dict = {
148 "clean" : (False, "Clean all files from the build directory"),
149 "debug" : (False, "Build the library in debug symbols"),
150 "builddir" : ("", "Directory where the build will be performed for autoconf builds."),
151 "prefix" : ("", "Configured prefix to use for autoconf builds. Defaults to installdir if set."),
152 "install" : (False, "Install the toolkit to the installdir directory, or the default dir."),
153 "installdir" : ("", "Directory where built wxWidgets will be installed"),
154 "mac_universal_binary" : (False, "Build Mac version as a universal binary"),
155 "mac_arch" : ("", "Build just the specified architecture on Mac"),
156 "mac_lipo" : (False, "EXPERIMENTAL: Create a universal binary by merging a PPC and Intel build together."),
157 "mac_framework" : (False, "Install the Mac build as a framework"),
158 "no_config" : (False, "Turn off configure step on autoconf builds"),
159 "config_only": (False, "Only run the configure step and then exit"),
160 "rebake" : (False, "Regenerate Bakefile and autoconf files"),
161 "unicode" : (False, "Build the library with unicode support"),
162 "wxpython" : (False, "Build the wxWidgets library with all options needed by wxPython"),
163 "cocoa" : (False, "Build the Cooca port (Mac only currently)."),
164 "osx_cocoa" : (False, "Build the new Cocoa port"),
165 "shared" : (False, "Build wx as a dynamic library"),
166 "cairo" : (False, "Build support for wxCairoContext (always true on GTK+)"),
167 "extra_make" : ("", "Extra args to pass on [n]make's command line."),
168 "features" : ("", "A comma-separated list of wxUSE_XYZ defines on Win, or a list of configure flags on unix."),
169 }
170
171 parser = optparse.OptionParser(usage="usage: %prog [options]", version="%prog 1.0")
172
173 for opt in option_dict:
174 default = option_dict[opt][0]
175
176 action = "store"
177 if type(default) == types.BooleanType:
178 action = "store_true"
179 parser.add_option("--" + opt, default=default, action=action, dest=opt, help=option_dict[opt][1])
180
181 options, arguments = parser.parse_args(args=args)
182
183 # compiler / build system specific args
184 buildDir = options.builddir
185 args = None
186 installDir = options.installdir
187 prefixDir = options.prefix
188
189 if toolkit == "autoconf":
190 if not buildDir:
191 buildDir = os.getcwd()
192 configure_opts = []
193 if options.features != "":
194 configure_opts.extend(options.features.split(" "))
195
196 if options.unicode:
197 configure_opts.append("--enable-unicode")
198
199 if options.debug:
200 configure_opts.append("--enable-debug")
201
202 if options.mac_universal_binary:
203 configure_opts.append("--enable-universal_binary")
204
205 if options.cocoa:
206 configure_opts.append("--with-old_cocoa")
207
208 if options.osx_cocoa:
209 configure_opts.append("--with-osx_cocoa")
210
211 if options.mac_arch:
212 configure_opts.append("--enable-macosx_arch=%s" % options.mac_arch)
213
214 wxpy_configure_opts = [
215 "--with-opengl",
216 "--enable-sound",
217 "--enable-graphics_ctx",
218 "--enable-mediactrl",
219 "--enable-display",
220 "--enable-geometry",
221 "--enable-debug_flag",
222 "--enable-optimise",
223 "--disable-debugreport",
224 "--enable-uiactionsim",
225 ]
226
227 if sys.platform.startswith("darwin"):
228 wxpy_configure_opts.append("--enable-monolithic")
229 else:
230 wxpy_configure_opts.append("--with-sdl")
231 wxpy_configure_opts.append("--with-gnomeprint")
232
233 # Ensure that the Carbon build stays compatible back to 10.4 and
234 # for the Cocoa build allow running on 10.5 and newer. We only add
235 # them to the wxpy options because this is a hard-requirement for
236 # wxPython, but other cases it is optional and is left up to the
237 # developer. TODO: there should be a command line option to set
238 # the SDK...
239 if sys.platform.startswith("darwin"):
240 if not options.osx_cocoa:
241 wxpy_configure_opts.append(
242 "--with-macosx-sdk=/Developer/SDKs/MacOSX10.4u.sdk")
243 else:
244 wxpy_configure_opts.append(
245 "--with-macosx-sdk=/Developer/SDKs/MacOSX10.5.sdk")
246
247
248 if not options.mac_framework:
249 if installDir and not prefixDir:
250 prefixDir = installDir
251 if prefixDir:
252 configure_opts.append("--prefix=" + prefixDir)
253
254 if options.wxpython:
255 configure_opts.extend(wxpy_configure_opts)
256 if options.debug:
257 # wxPython likes adding these debug options too
258 configure_opts.append("--enable-debug_gdb")
259 configure_opts.append("--disable-optimise")
260
261 if options.rebake:
262 retval = os.system("make -f autogen.mk")
263 exitIfError(retval, "Error running autogen.mk")
264
265 if options.mac_framework:
266 # Framework build is always a universal binary
267 options.mac_lipo = True
268 name = "wx"
269 if options.osx_cocoa:
270 name += "OSXCocoa"
271 installDir = "/Library/Frameworks/%s.framework/Versions/%s" % (name, getWxRelease())
272 configure_opts.append("--prefix=" + installDir)
273 # framework builds always need to be monolithic
274 if not "--enable-monolithic" in configure_opts:
275 configure_opts.append("--enable-monolithic")
276
277 print "Configure options: " + `configure_opts`
278 wxBuilder = builder.AutoconfBuilder()
279 if not options.no_config and not options.clean and not options.mac_lipo:
280 olddir = os.getcwd()
281 if buildDir:
282 os.chdir(buildDir)
283 exitIfError(wxBuilder.configure(dir=wxRootDir, options=configure_opts),
284 "Error running configure")
285 os.chdir(olddir)
286
287 if options.config_only:
288 print "Exiting after configure"
289 return
290
291 elif toolkit in ["msvc", "msvcProject"]:
292 flags = {}
293 buildDir = os.path.abspath(os.path.join(scriptDir, "..", "msw"))
294
295 print "creating wx/msw/setup.h from setup0.h"
296 if options.unicode:
297 flags["wxUSE_UNICODE"] = "1"
298 if VERSION < (2,9):
299 flags["wxUSE_UNICODE_MSLU"] = "1"
300
301 if options.cairo:
302 flags["wxUSE_CAIRO"] = "1"
303
304 if options.wxpython:
305 flags["wxDIALOG_UNIT_COMPATIBILITY "] = "0"
306 flags["wxUSE_DEBUGREPORT"] = "0"
307 flags["wxUSE_DIALUP_MANAGER"] = "0"
308 flags["wxUSE_GRAPHICS_CONTEXT"] = "1"
309 flags["wxUSE_DISPLAY"] = "1"
310 flags["wxUSE_GLCANVAS"] = "1"
311 flags["wxUSE_POSTSCRIPT"] = "1"
312 flags["wxUSE_AFM_FOR_POSTSCRIPT"] = "0"
313 flags["wxUSE_DATEPICKCTRL_GENERIC"] = "1"
314
315 if VERSION < (2,9):
316 flags["wxUSE_DIB_FOR_BITMAP"] = "1"
317
318 if VERSION >= (2,9):
319 flags["wxUSE_UIACTIONSIMULATOR"] = "1"
320
321
322 mswIncludeDir = os.path.join(wxRootDir, "include", "wx", "msw")
323 setup0File = os.path.join(mswIncludeDir, "setup0.h")
324 setupText = open(setup0File, "rb").read()
325
326 for flag in flags:
327 setupText, subsMade = re.subn(flag + "\s+?\d", "%s %s" % (flag, flags[flag]), setupText)
328 if subsMade == 0:
329 print "Flag %s wasn't found in setup0.h!" % flag
330 sys.exit(1)
331
332 setupFile = open(os.path.join(mswIncludeDir, "setup.h"), "wb")
333 setupFile.write(setupText)
334 setupFile.close()
335 args = []
336 if toolkit == "msvc":
337 print "setting build options..."
338 args.append("-f makefile.vc")
339 if options.unicode:
340 args.append("UNICODE=1")
341 if VERSION < (2,9):
342 args.append("MSLU=1")
343
344 if options.wxpython:
345 args.append("OFFICIAL_BUILD=1")
346 args.append("SHARED=1")
347 args.append("MONOLITHIC=0")
348 args.append("USE_OPENGL=1")
349 args.append("USE_GDIPLUS=1")
350
351 if not options.debug:
352 args.append("BUILD=release")
353 else:
354 args.append("BUILD=debug")
355
356 wxBuilder = builder.MSVCBuilder()
357
358 if toolkit == "msvcProject":
359 args = []
360 if options.shared or options.wxpython:
361 args.append("wx_dll.dsw")
362 else:
363 args.append("wx.dsw")
364
365 # TODO:
366 wxBuilder = builder.MSVCProjectBuilder()
367
368 if not wxBuilder:
369 print "Builder not available for your specified platform/compiler."
370 sys.exit(1)
371
372 if options.clean:
373 print "Performing cleanup."
374 wxBuilder.clean()
375
376 if options.wxpython:
377 exitIfError(wxBuilder.clean(os.path.join(contribDir, "gizmos")), "Error building gizmos")
378 exitIfError(wxBuilder.clean(os.path.join(contribDir, "stc")), "Error building stc")
379
380 sys.exit(0)
381
382 isLipo = False
383 if options.mac_lipo:
384 if options.mac_universal_binary:
385 print "WARNING: Cannot specify both mac_lipo and mac_universal_binary, as they conflict."
386 print " Using mac_universal_binary..."
387 else:
388 isLipo = True
389 # TODO: Add 64-bit when we're building OS X Cocoa
390
391 # 2.8, use gcc 3.3 on PPC for 10.3 support, but only when building ...
392 macVersion = platform.mac_ver()[0]
393 isLeopard = macVersion.find("10.5") != -1
394
395 if not isLeopard and os.path.exists(os.path.join(wxRootDir, contribDir)):
396 # Building wx 2.8 so make the ppc build compatible with Panther
397 doMacLipoBuild("ppc", buildDir, installDir, cxxcompiler="g++-3.3", cccompiler="gcc-3.3",
398 target="10.3", flags="-DMAC_OS_X_VERSION_MAX_ALLOWED=1040")
399 else:
400 doMacLipoBuild("ppc", buildDir, installDir)
401
402 doMacLipoBuild("i386", buildDir, installDir)
403
404 # Use lipo to merge together all binaries in the install dirs, and it
405 # also copies all other files and links it finds to the new destination.
406 result = os.system("python %s/distrib/scripts/mac/lipo-dir.py %s %s %s" %
407 (wxRootDir, installDir+"/ppc", installDir+"/i386", installDir))
408
409 # tweak the wx-config script
410 fname = os.path.abspath(installDir + '/bin/wx-config')
411 data = open(fname).read()
412 data = data.replace('ppc/', '')
413 data = data.replace('i386/', '')
414 open(fname, 'w').write(data)
415
416 shutil.rmtree(installDir + "/ppc")
417 shutil.rmtree(installDir + "/i386")
418
419
420
421 if not isLipo:
422 if options.extra_make:
423 args.append(options.extra_make)
424 exitIfError(wxBuilder.build(dir=buildDir, options=args), "Error building")
425
426 if options.wxpython and os.path.exists(contribDir):
427 exitIfError(wxBuilder.build(os.path.join(contribDir, "gizmos"), options=args), "Error building gizmos")
428 exitIfError(wxBuilder.build(os.path.join(contribDir, "stc"),options=args), "Error building stc")
429
430 if options.install:
431 extra=None
432 if installDir:
433 extra = ['DESTDIR='+installDir]
434 wxBuilder.install(options=extra)
435
436 if options.wxpython and os.path.exists(contribDir):
437 exitIfError(wxBuilder.install(os.path.join(contribDir, "gizmos"), options=extra), "Error building gizmos")
438 exitIfError(wxBuilder.install(os.path.join(contribDir, "stc"), options=extra), "Error building stc")
439
440 if options.mac_framework:
441
442 def renameLibrary(libname, frameworkname):
443 reallib = libname
444 links = []
445 while os.path.islink(reallib):
446 links.append(reallib)
447 reallib = "lib/" + os.readlink(reallib)
448
449 print "reallib is %s" % reallib
450 os.system("mv -f %s lib/%s.dylib" % (reallib, frameworkname))
451
452 for link in links:
453 os.system("ln -s -f %s.dylib %s" % (frameworkname, link))
454
455 os.chdir(installDir)
456 build_string = ""
457 if options.debug:
458 build_string = "d"
459 version = commands.getoutput("bin/wx-config --release")
460 basename = commands.getoutput("bin/wx-config --basename")
461 configname = commands.getoutput("bin/wx-config --selected-config")
462
463 os.system("ln -s -f bin Resources")
464
465 # we make wx the "actual" library file and link to it from libwhatever.dylib
466 # so that things can link to wx and survive minor version changes
467 renameLibrary("lib/lib%s-%s.dylib" % (basename, version), "wx")
468 os.system("ln -s -f lib/wx.dylib wx")
469
470 os.system("ln -s -f include/wx Headers")
471
472 for lib in ["GL", "STC", "Gizmos", "Gizmos_xrc"]:
473 libfile = "lib/lib%s_%s-%s.dylib" % (basename, lib.lower(), version)
474 if os.path.exists(libfile):
475 frameworkDir = "framework/wx%s/%s" % (lib, version)
476 if not os.path.exists(frameworkDir):
477 os.makedirs(frameworkDir)
478 renameLibrary(libfile, "wx" + lib)
479 os.system("ln -s -f ../../../%s %s/wx%s" % (libfile, frameworkDir, lib))
480
481 for lib in glob.glob("lib/*.dylib"):
482 if not os.path.islink(lib):
483 corelibname = "lib/lib%s-%s.0.dylib" % (basename, version)
484 os.system("install_name_tool -id %s %s" % (os.path.join(installDir, lib), lib))
485 os.system("install_name_tool -change %s %s %s" % (os.path.join(installDir, "i386", corelibname), os.path.join(installDir, corelibname), lib))
486
487 os.chdir("include")
488
489 header_template = """
490
491 #ifndef __WX_FRAMEWORK_HEADER__
492 #define __WX_FRAMEWORK_HEADER__
493
494 %s
495
496 #endif // __WX_FRAMEWORK_HEADER__
497 """
498 headers = ""
499 header_dir = "wx-%s/wx" % version
500 for include in glob.glob(header_dir + "/*.h"):
501 headers += "wx/" + os.path.basename(include) + "\n"
502
503 framework_header = open("wx.h", "w")
504 framework_header.write(header_template % headers)
505 framework_header.close()
506
507 os.system("ln -s -f %s wx" % header_dir)
508 os.system("ln -s -f ../../../lib/wx/include/%s/wx/setup.h wx/setup.h" % configname)
509
510 os.chdir(os.path.join(installDir, "..", ".."))
511 os.system("ln -s -f %s Versions/Current" % os.path.basename(installDir))
512 os.system("ln -s -f Versions/Current/Headers Headers")
513 os.system("ln -s -f Versions/Current/Resources Resources")
514 os.system("ln -s -f Versions/Current/wx wx")
515
516
517 # adjust the install_name if needed TODO: skip this for framework builds?
518 if sys.platform.startswith("darwin") and \
519 options.install and \
520 options.installdir and \
521 not options.wxpython: # wxPython's build will do this later if needed
522 prefix = options.prefix
523 if not prefix:
524 prefix = '/usr/local'
525 macFixupInstallNames(options.installdir, prefix)#, buildDir)
526
527
528
529 if __name__ == '__main__':
530 exitWithException = False # use sys.exit instead
531 main(sys.argv[0], sys.argv[1:])
532