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