10.5 is the minimum SDK for both OSX ports now
[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 import subprocess
19
20 # builder object
21 wxBuilder = None
22
23 # other globals
24 scriptDir = None
25 wxRootDir = None
26 contribDir = None
27 options = None
28 configure_opts = None
29 exitWithException = True
30
31 verbose = False
32
33
34 def numCPUs():
35 """
36 Detects the number of CPUs on a system.
37 This approach is from detectCPUs here: http://www.artima.com/weblogs/viewpost.jsp?thread=230001
38 """
39 # Linux, Unix and MacOS:
40 if hasattr(os, "sysconf"):
41 if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"):
42 # Linux & Unix:
43 ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
44 if isinstance(ncpus, int) and ncpus > 0:
45 return ncpus
46 else: # OSX:
47 p = subprocess.Popen("sysctl -n hw.ncpu", shell=True, stdout=subprocess.PIPE)
48 return p.stdout.read()
49
50 # Windows:
51 if os.environ.has_key("NUMBER_OF_PROCESSORS"):
52 ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]);
53 if ncpus > 0:
54 return ncpus
55 return 1 # Default
56
57
58 def getXcodePath():
59 p = subprocess.Popen("xcode-select -print-path", shell=True, stdout=subprocess.PIPE)
60 output = p.stdout.read()
61 return output.strip()
62
63
64 def exitIfError(code, msg):
65 if code != 0:
66 print msg
67 if exitWithException:
68 raise builder.BuildError, msg
69 else:
70 sys.exit(1)
71
72
73 def getWxRelease(wxRoot=None):
74 if not wxRoot:
75 global wxRootDir
76 wxRoot = wxRootDir
77
78 configureText = open(os.path.join(wxRoot, "configure.in"), "r").read()
79 majorVersion = re.search("wx_major_version_number=(\d+)", configureText).group(1)
80 minorVersion = re.search("wx_minor_version_number=(\d+)", configureText).group(1)
81
82 versionText = "%s.%s" % (majorVersion, minorVersion)
83
84 if int(minorVersion) % 2:
85 releaseVersion = re.search("wx_release_number=(\d+)", configureText).group(1)
86 versionText += ".%s" % (releaseVersion)
87
88 return versionText
89
90
91 def getFrameworkName(options):
92 # the name of the framework is based on the wx port being built
93 name = "wxOSX"
94 if options.osx_cocoa:
95 name += "Cocoa"
96 else:
97 name += "Carbon"
98 return name
99
100
101 def getPrefixInFramework(options, wxRoot=None):
102 # the path inside the framework that is the wx --prefix
103 fwPrefix = os.path.join(
104 os.path.abspath(options.mac_framework_prefix),
105 "%s.framework/Versions/%s" % (getFrameworkName(options), getWxRelease(wxRoot)))
106 return fwPrefix
107
108
109 def macFixupInstallNames(destdir, prefix, buildDir=None):
110 # When an installdir is used then the install_names embedded in
111 # the dylibs are not correct. Reset the IDs and the dependencies
112 # to use just the prefix.
113 print "**** macFixupInstallNames(%s, %s, %s)" % (destdir, prefix, buildDir)
114 pwd = os.getcwd()
115 os.chdir(destdir+prefix+'/lib')
116 dylibs = glob.glob('*.dylib') # ('*[0-9].[0-9].[0-9].[0-9]*.dylib')
117 for lib in dylibs:
118 cmd = 'install_name_tool -id %s/lib/%s %s/lib/%s' % \
119 (prefix,lib, destdir+prefix,lib)
120 print cmd
121 run(cmd)
122 for dep in dylibs:
123 if buildDir is not None:
124 cmd = 'install_name_tool -change %s/lib/%s %s/lib/%s %s/lib/%s' % \
125 (buildDir,dep, prefix,dep, destdir+prefix,lib)
126 else:
127 cmd = 'install_name_tool -change %s/lib/%s %s/lib/%s %s/lib/%s' % \
128 (destdir+prefix,dep, prefix,dep, destdir+prefix,lib)
129 print cmd
130 run(cmd)
131 os.chdir(pwd)
132
133
134 def run(cmd):
135 global verbose
136 if verbose:
137 print "Running %s" % cmd
138 return exitIfError(os.system(cmd), "Error running %s" % cmd)
139
140
141 def main(scriptName, args):
142 global scriptDir
143 global wxRootDir
144 global contribDir
145 global options
146 global configure_opts
147 global wxBuilder
148
149 scriptDir = os.path.dirname(os.path.abspath(scriptName))
150 wxRootDir = os.path.abspath(os.path.join(scriptDir, "..", ".."))
151
152 contribDir = os.path.join("contrib", "src")
153 installDir = None
154
155 VERSION = tuple([int(i) for i in getWxRelease().split('.')[:2]])
156
157 if sys.platform.startswith("win"):
158 contribDir = os.path.join(wxRootDir, "contrib", "build")
159
160 if sys.platform.startswith("win"):
161 toolkit = "msvc"
162 else:
163 toolkit = "autoconf"
164
165 defJobs = str(numCPUs())
166 defFwPrefix = '/Library/Frameworks'
167
168 option_dict = {
169 "clean" : (False, "Clean all files from the build directory"),
170 "debug" : (False, "Build the library in debug symbols"),
171 "builddir" : ("", "Directory where the build will be performed for autoconf builds."),
172 "prefix" : ("", "Configured prefix to use for autoconf builds. Defaults to installdir if set. Ignored for framework builds."),
173 "jobs" : (defJobs, "Number of jobs to run at one time in make. Default: %s" % defJobs),
174 "install" : (False, "Install the toolkit to the installdir directory, or the default dir."),
175 "installdir" : ("", "Directory where built wxWidgets will be installed"),
176 "mac_distdir" : (None, "If set on Mac, will create an installer package in the specified dir."),
177 "mac_universal_binary"
178 : ("", "Comma separated list of architectures to include in the Mac universal binary"),
179 "mac_framework" : (False, "Install the Mac build as a framework"),
180 "mac_framework_prefix"
181 : (defFwPrefix, "Prefix where the framework should be installed. Default: %s" % defFwPrefix),
182 "cairo" : (False, "Enable dynamicly loading the Cairo lib for wxGraphicsContext on MSW"),
183 "no_config" : (False, "Turn off configure step on autoconf builds"),
184 "config_only" : (False, "Only run the configure step and then exit"),
185 "rebake" : (False, "Regenerate Bakefile and autoconf files"),
186 "unicode" : (False, "Build the library with unicode support"),
187 "wxpython" : (False, "Build the wxWidgets library with all options needed by wxPython"),
188 "cocoa" : (False, "Build the old Mac Cooca port."),
189 "osx_cocoa" : (False, "Build the new Cocoa port"),
190 "shared" : (False, "Build wx as a dynamic library"),
191 "extra_make" : ("", "Extra args to pass on [n]make's command line."),
192 "features" : ("", "A comma-separated list of wxUSE_XYZ defines on Win, or a list of configure flags on unix."),
193 "verbose" : (False, "Print commands as they are run, (to aid with debugging this script)"),
194 }
195
196 parser = optparse.OptionParser(usage="usage: %prog [options]", version="%prog 1.0")
197
198 keys = option_dict.keys()
199 keys.sort()
200 for opt in keys:
201 default = option_dict[opt][0]
202 action = "store"
203 if type(default) == types.BooleanType:
204 action = "store_true"
205 parser.add_option("--" + opt, default=default, action=action, dest=opt,
206 help=option_dict[opt][1])
207
208 options, arguments = parser.parse_args(args=args)
209
210 global verbose
211 if options.verbose:
212 verbose = True
213
214 # compiler / build system specific args
215 buildDir = options.builddir
216 args = []
217 installDir = options.installdir
218 prefixDir = options.prefix
219
220 if toolkit == "autoconf":
221 if not buildDir:
222 buildDir = os.getcwd()
223 configure_opts = []
224 if options.features != "":
225 configure_opts.extend(options.features.split(" "))
226
227 if options.unicode:
228 configure_opts.append("--enable-unicode")
229
230 if options.debug:
231 configure_opts.append("--enable-debug")
232
233 if options.cocoa:
234 configure_opts.append("--with-old_cocoa")
235
236 if options.osx_cocoa:
237 configure_opts.append("--with-osx_cocoa")
238
239 wxpy_configure_opts = [
240 "--with-opengl",
241 "--enable-sound",
242 "--enable-graphics_ctx",
243 "--enable-mediactrl",
244 "--enable-display",
245 "--enable-geometry",
246 "--enable-debug_flag",
247 "--enable-optimise",
248 "--disable-debugreport",
249 "--enable-uiactionsim",
250 ]
251
252 if sys.platform.startswith("darwin"):
253 wxpy_configure_opts.append("--enable-monolithic")
254 else:
255 wxpy_configure_opts.append("--with-sdl")
256 wxpy_configure_opts.append("--with-gnomeprint")
257
258 # Ensure that the Carbon build stays compatible back to 10.4 and
259 # for the Cocoa build allow running on 10.5 and newer. We only add
260 # them to the wxpy options because this is a hard-requirement for
261 # wxPython, but other cases it is optional and is left up to the
262 # developer. TODO: there should be a command line option to set
263 # the SDK...
264 if sys.platform.startswith("darwin"):
265 xcodePath = getXcodePath()
266 sdks = [
267 xcodePath+"/SDKs/MacOSX10.5.sdk",
268 xcodePath+"/SDKs/MacOSX10.6.sdk",
269 xcodePath+"/SDKs/MacOSX10.7.sdk",
270 ]
271
272 # use the lowest available sdk
273 for sdk in sdks:
274 if os.path.exists(sdk):
275 wxpy_configure_opts.append(
276 "--with-macosx-sdk=%s" % sdk)
277 break
278
279 if not options.mac_framework:
280 if installDir and not prefixDir:
281 prefixDir = installDir
282 if prefixDir:
283 prefixDir = os.path.abspath(prefixDir)
284 configure_opts.append("--prefix=" + prefixDir)
285
286
287 if options.wxpython:
288 configure_opts.extend(wxpy_configure_opts)
289 if options.debug:
290 # wxPython likes adding these debug options too
291 configure_opts.append("--enable-debug_gdb")
292 configure_opts.append("--disable-optimise")
293 configure_opts.remove("--enable-optimise")
294
295
296 if options.rebake:
297 retval = run("make -f autogen.mk")
298 exitIfError(retval, "Error running autogen.mk")
299
300 if options.mac_framework:
301 # TODO: Should options.install be automatically turned on if the
302 # mac_framework flag is given?
303
304 # framework builds always need to be monolithic
305 if not "--enable-monolithic" in configure_opts:
306 configure_opts.append("--enable-monolithic")
307
308 # The --prefix given to configure will be the framework prefix
309 # plus the framework specific dir structure.
310 prefixDir = getPrefixInFramework(options)
311 configure_opts.append("--prefix=" + prefixDir)
312
313 # the framework build adds symlinks above the installDir + prefixDir folder
314 # so we need to wipe from the framework root instead of inside the prefixDir.
315 frameworkRootDir = os.path.abspath(os.path.join(installDir + prefixDir, "..", ".."))
316 if os.path.exists(frameworkRootDir):
317 if os.path.exists(frameworkRootDir):
318 shutil.rmtree(frameworkRootDir)
319
320 if options.mac_universal_binary:
321 configure_opts.append("--enable-universal_binary=%s" % options.mac_universal_binary)
322
323
324 print "Configure options: " + `configure_opts`
325 wxBuilder = builder.AutoconfBuilder()
326 if not options.no_config and not options.clean:
327 olddir = os.getcwd()
328 if buildDir:
329 os.chdir(buildDir)
330 exitIfError(wxBuilder.configure(dir=wxRootDir, options=configure_opts),
331 "Error running configure")
332 os.chdir(olddir)
333
334 if options.config_only:
335 print "Exiting after configure"
336 return
337
338 elif toolkit in ["msvc", "msvcProject"]:
339 flags = {}
340 buildDir = os.path.abspath(os.path.join(scriptDir, "..", "msw"))
341
342 print "creating wx/msw/setup.h from setup0.h"
343 if options.unicode:
344 flags["wxUSE_UNICODE"] = "1"
345 if VERSION < (2,9):
346 flags["wxUSE_UNICODE_MSLU"] = "1"
347
348 if options.cairo:
349 if not os.environ.get("CAIRO_ROOT"):
350 print "WARNING: Expected CAIRO_ROOT set in the environment!"
351 flags["wxUSE_CAIRO"] = "1"
352
353 if options.wxpython:
354 flags["wxDIALOG_UNIT_COMPATIBILITY "] = "0"
355 flags["wxUSE_DEBUGREPORT"] = "0"
356 flags["wxUSE_DIALUP_MANAGER"] = "0"
357 flags["wxUSE_GRAPHICS_CONTEXT"] = "1"
358 flags["wxUSE_DISPLAY"] = "1"
359 flags["wxUSE_GLCANVAS"] = "1"
360 flags["wxUSE_POSTSCRIPT"] = "1"
361 flags["wxUSE_AFM_FOR_POSTSCRIPT"] = "0"
362 flags["wxUSE_DATEPICKCTRL_GENERIC"] = "1"
363
364 if VERSION < (2,9):
365 flags["wxUSE_DIB_FOR_BITMAP"] = "1"
366
367 if VERSION >= (2,9):
368 flags["wxUSE_UIACTIONSIMULATOR"] = "1"
369
370
371 mswIncludeDir = os.path.join(wxRootDir, "include", "wx", "msw")
372 setup0File = os.path.join(mswIncludeDir, "setup0.h")
373 setupText = open(setup0File, "rb").read()
374
375 for flag in flags:
376 setupText, subsMade = re.subn(flag + "\s+?\d", "%s %s" % (flag, flags[flag]), setupText)
377 if subsMade == 0:
378 print "Flag %s wasn't found in setup0.h!" % flag
379 sys.exit(1)
380
381 setupFile = open(os.path.join(mswIncludeDir, "setup.h"), "wb")
382 setupFile.write(setupText)
383 setupFile.close()
384 args = []
385 if toolkit == "msvc":
386 print "setting build options..."
387 args.append("-f makefile.vc")
388 if options.unicode:
389 args.append("UNICODE=1")
390 if VERSION < (2,9):
391 args.append("MSLU=1")
392
393 if options.wxpython:
394 args.append("OFFICIAL_BUILD=1")
395 args.append("SHARED=1")
396 args.append("MONOLITHIC=0")
397 args.append("USE_OPENGL=1")
398 args.append("USE_GDIPLUS=1")
399
400 if not options.debug:
401 args.append("BUILD=release")
402 else:
403 args.append("BUILD=debug")
404
405 if options.shared:
406 args.append("SHARED=1")
407
408 if options.cairo:
409 args.append(
410 "CPPFLAGS=/I%s" %
411 os.path.join(os.environ.get("CAIRO_ROOT", ""), 'include\\cairo'))
412
413 wxBuilder = builder.MSVCBuilder()
414
415 if toolkit == "msvcProject":
416 args = []
417 if options.shared or options.wxpython:
418 args.append("wx_dll.dsw")
419 else:
420 args.append("wx.dsw")
421
422 # TODO:
423 wxBuilder = builder.MSVCProjectBuilder()
424
425
426 if not wxBuilder:
427 print "Builder not available for your specified platform/compiler."
428 sys.exit(1)
429
430 if options.clean:
431 print "Performing cleanup."
432 wxBuilder.clean(dir=buildDir, options=args)
433
434 sys.exit(0)
435
436 if options.extra_make:
437 args.append(options.extra_make)
438
439 if not sys.platform.startswith("win"):
440 args.append("--jobs=" + options.jobs)
441 exitIfError(wxBuilder.build(dir=buildDir, options=args), "Error building")
442
443 if options.install:
444 extra=None
445 if installDir:
446 extra = ['DESTDIR='+installDir]
447 wxBuilder.install(dir=buildDir, options=extra)
448
449 if options.install and options.mac_framework:
450
451 def renameLibrary(libname, frameworkname):
452 reallib = libname
453 links = []
454 while os.path.islink(reallib):
455 links.append(reallib)
456 reallib = "lib/" + os.readlink(reallib)
457
458 #print "reallib is %s" % reallib
459 run("mv -f %s lib/%s.dylib" % (reallib, frameworkname))
460
461 for link in links:
462 run("ln -s -f %s.dylib %s" % (frameworkname, link))
463
464 frameworkRootDir = prefixDir
465 if installDir:
466 print "installDir = %s" % installDir
467 frameworkRootDir = installDir + prefixDir
468 os.chdir(frameworkRootDir)
469 build_string = ""
470 if options.debug:
471 build_string = "d"
472
473 fwname = getFrameworkName(options)
474 version = commands.getoutput("bin/wx-config --release")
475 version_full = commands.getoutput("bin/wx-config --version")
476 basename = commands.getoutput("bin/wx-config --basename")
477 configname = commands.getoutput("bin/wx-config --selected-config")
478
479 os.makedirs("Resources")
480 wxplist = dict(
481 CFBundleDevelopmentRegion="English",
482 CFBundleIdentifier='org.wxwidgets.wxosxcocoa',
483 CFBundleName=fwname,
484 CFBundleVersion=version_full,
485 CFBundleExecutable=fwname,
486 CFBundleGetInfoString="%s %s" % (fwname, version_full),
487 CFBundlePackageType="FMWK",
488 CFBundleSignature="WXCO",
489 CFBundleShortVersionString=version_full,
490 CFBundleInfoDictionaryVersion="6.0",
491 )
492
493 import plistlib
494 plistlib.writePlist(wxplist, os.path.join(frameworkRootDir, "Resources", "Info.plist"))
495
496 # we make wx the "actual" library file and link to it from libwhatever.dylib
497 # so that things can link to wx and survive minor version changes
498 renameLibrary("lib/lib%s-%s.dylib" % (basename, version), fwname)
499 run("ln -s -f lib/%s.dylib %s" % (fwname, fwname))
500
501 run("ln -s -f include Headers")
502
503 for lib in ["GL", "STC", "Gizmos", "Gizmos_xrc"]:
504 libfile = "lib/lib%s_%s-%s.dylib" % (basename, lib.lower(), version)
505 if os.path.exists(libfile):
506 frameworkDir = "framework/wx%s/%s" % (lib, version)
507 if not os.path.exists(frameworkDir):
508 os.makedirs(frameworkDir)
509 renameLibrary(libfile, "wx" + lib)
510 run("ln -s -f ../../../%s %s/wx%s" % (libfile, frameworkDir, lib))
511
512 for lib in glob.glob("lib/*.dylib"):
513 if not os.path.islink(lib):
514 corelibname = "lib/lib%s-%s.0.dylib" % (basename, version)
515 run("install_name_tool -id %s %s" % (os.path.join(prefixDir, lib), lib))
516 run("install_name_tool -change %s %s %s" % (os.path.join(frameworkRootDir, corelibname), os.path.join(prefixDir, corelibname), lib))
517
518 os.chdir("include")
519
520 header_template = """
521 #ifndef __WX_FRAMEWORK_HEADER__
522 #define __WX_FRAMEWORK_HEADER__
523
524 %s
525
526 #endif // __WX_FRAMEWORK_HEADER__
527 """
528 headers = ""
529 header_dir = "wx-%s/wx" % version
530 for include in glob.glob(header_dir + "/*.h"):
531 headers += "#include <wx/" + os.path.basename(include) + ">\n"
532
533 framework_header = open("%s.h" % fwname, "w")
534 framework_header.write(header_template % headers)
535 framework_header.close()
536
537 run("ln -s -f %s wx" % header_dir)
538 os.chdir("wx-%s/wx" % version)
539 run("ln -s -f ../../../lib/wx/include/%s/wx/setup.h setup.h" % configname)
540
541 os.chdir(os.path.join(frameworkRootDir, ".."))
542 run("ln -s -f %s Current" % getWxRelease())
543 os.chdir("..")
544 run("ln -s -f Versions/Current/Headers Headers")
545 run("ln -s -f Versions/Current/Resources Resources")
546 run("ln -s -f Versions/Current/%s %s" % (fwname, fwname))
547
548 # sanity check to ensure the symlink works
549 os.chdir("Versions/Current")
550
551 # put info about the framework into wx-config
552 os.chdir(frameworkRootDir)
553 text = file('lib/wx/config/%s' % configname).read()
554 text = text.replace("MAC_FRAMEWORK=", "MAC_FRAMEWORK=%s" % getFrameworkName(options))
555 if options.mac_framework_prefix not in ['/Library/Frameworks',
556 '/System/Library/Frameworks']:
557 text = text.replace("MAC_FRAMEWORK_PREFIX=",
558 "MAC_FRAMEWORK_PREFIX=%s" % options.mac_framework_prefix)
559 file('lib/wx/config/%s' % configname, 'w').write(text)
560
561 # The framework is finished!
562 print "wxWidgets framework created at: " + \
563 os.path.join( installDir,
564 options.mac_framework_prefix,
565 '%s.framework' % fwname)
566
567
568 # adjust the install_name if needed
569 if sys.platform.startswith("darwin") and \
570 options.install and \
571 options.installdir and \
572 not options.mac_framework and \
573 not options.wxpython: # wxPython's build will do this later if needed
574 if not prefixDir:
575 prefixDir = '/usr/local'
576 macFixupInstallNames(options.installdir, prefixDir)#, buildDir)
577
578 # make a package if a destdir was set.
579 if options.mac_framework and \
580 options.install and \
581 options.installdir and \
582 options.mac_distdir:
583
584 if os.path.exists(options.mac_distdir):
585 shutil.rmtree(options.mac_distdir)
586
587 packagedir = os.path.join(options.mac_distdir, "packages")
588 os.makedirs(packagedir)
589 basename = os.path.basename(prefixDir.split(".")[0])
590 packageName = basename + "-" + getWxRelease()
591 packageMakerPath = getXcodePath()+"/usr/bin/packagemaker "
592 args = []
593 args.append("--root %s" % options.installdir)
594 args.append("--id org.wxwidgets.%s" % basename.lower())
595 args.append("--title %s" % packageName)
596 args.append("--version %s" % getWxRelease())
597 args.append("--out %s" % os.path.join(packagedir, packageName + ".pkg"))
598 cmd = packageMakerPath + ' '.join(args)
599 print "cmd = %s" % cmd
600 run(cmd)
601
602 os.chdir(options.mac_distdir)
603
604 run('hdiutil create -srcfolder %s -volname "%s" -imagekey zlib-level=9 %s.dmg' % (packagedir, packageName, packageName))
605
606 shutil.rmtree(packagedir)
607
608 if __name__ == '__main__':
609 exitWithException = False # use sys.exit instead
610 main(sys.argv[0], sys.argv[1:])
611