Merge in from trunk r67662 to r64801
[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 "no_config" : (False, "Turn off configure step on autoconf builds"),
178 "config_only" : (False, "Only run the configure step and then exit"),
179 "rebake" : (False, "Regenerate Bakefile and autoconf files"),
180 "unicode" : (False, "Build the library with unicode support"),
181 "wxpython" : (False, "Build the wxWidgets library with all options needed by wxPython"),
182 "cocoa" : (False, "Build the old Mac Cooca port."),
183 "osx_cocoa" : (False, "Build the new Cocoa port"),
184 "shared" : (False, "Build wx as a dynamic library"),
185 "cairo" : (False, "Build support for wxCairoContext (always true on GTK+)"),
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 if not options.osx_cocoa:
264 wxpy_configure_opts.append(
265 "--with-macosx-sdk=/Developer/SDKs/MacOSX10.4u.sdk")
266 else:
267 wxpy_configure_opts.append(
268 "--with-macosx-sdk=/Developer/SDKs/MacOSX10.5.sdk")
269
270
271 if not options.mac_framework:
272 if installDir and not prefixDir:
273 prefixDir = installDir
274 if prefixDir:
275 prefixDir = os.path.abspath(prefixDir)
276 configure_opts.append("--prefix=" + prefixDir)
277
278
279 if options.wxpython:
280 configure_opts.extend(wxpy_configure_opts)
281 if options.debug:
282 # wxPython likes adding these debug options too
283 configure_opts.append("--enable-debug_gdb")
284 configure_opts.append("--disable-optimise")
285 configure_opts.remove("--enable-optimise")
286
287
288 if options.rebake:
289 retval = run("make -f autogen.mk")
290 exitIfError(retval, "Error running autogen.mk")
291
292 if options.mac_framework:
293 # TODO: Should options.install be automatically turned on if the
294 # mac_framework flag is given?
295
296 # The framework build is always a universal binary, unless we are
297 # explicitly told to build only one architecture
298 if not options.mac_arch:
299 options.mac_universal_binary = True
300
301 # framework builds always need to be monolithic
302 if not "--enable-monolithic" in configure_opts:
303 configure_opts.append("--enable-monolithic")
304
305 # The --prefix given to configure will be the framework prefix
306 # plus the framework specific dir structure.
307 prefixDir = getPrefixInFramework(options)
308 configure_opts.append("--prefix=" + prefixDir)
309
310 # the framework build adds symlinks above the installDir + prefixDir folder
311 # so we need to wipe from the framework root instead of inside the prefixDir.
312 frameworkRootDir = os.path.abspath(os.path.join(installDir + prefixDir, "..", ".."))
313 if os.path.exists(frameworkRootDir):
314 if os.path.exists(frameworkRootDir):
315 shutil.rmtree(frameworkRootDir)
316
317 if options.mac_universal_binary:
318 configure_opts.append("--enable-universal_binary")
319
320
321 print "Configure options: " + `configure_opts`
322 wxBuilder = builder.AutoconfBuilder()
323 if not options.no_config and not options.clean:
324 olddir = os.getcwd()
325 if buildDir:
326 os.chdir(buildDir)
327 exitIfError(wxBuilder.configure(dir=wxRootDir, options=configure_opts),
328 "Error running configure")
329 os.chdir(olddir)
330
331 if options.config_only:
332 print "Exiting after configure"
333 return
334
335 elif toolkit in ["msvc", "msvcProject"]:
336 flags = {}
337 buildDir = os.path.abspath(os.path.join(scriptDir, "..", "msw"))
338
339 print "creating wx/msw/setup.h from setup0.h"
340 if options.unicode:
341 flags["wxUSE_UNICODE"] = "1"
342 if VERSION < (2,9):
343 flags["wxUSE_UNICODE_MSLU"] = "1"
344
345 if options.cairo:
346 flags["wxUSE_CAIRO"] = "1"
347
348 if options.wxpython:
349 flags["wxDIALOG_UNIT_COMPATIBILITY "] = "0"
350 flags["wxUSE_DEBUGREPORT"] = "0"
351 flags["wxUSE_DIALUP_MANAGER"] = "0"
352 flags["wxUSE_GRAPHICS_CONTEXT"] = "1"
353 flags["wxUSE_DISPLAY"] = "1"
354 flags["wxUSE_GLCANVAS"] = "1"
355 flags["wxUSE_POSTSCRIPT"] = "1"
356 flags["wxUSE_AFM_FOR_POSTSCRIPT"] = "0"
357 flags["wxUSE_DATEPICKCTRL_GENERIC"] = "1"
358
359 if VERSION < (2,9):
360 flags["wxUSE_DIB_FOR_BITMAP"] = "1"
361
362 if VERSION >= (2,9):
363 flags["wxUSE_UIACTIONSIMULATOR"] = "1"
364
365
366 mswIncludeDir = os.path.join(wxRootDir, "include", "wx", "msw")
367 setup0File = os.path.join(mswIncludeDir, "setup0.h")
368 setupText = open(setup0File, "rb").read()
369
370 for flag in flags:
371 setupText, subsMade = re.subn(flag + "\s+?\d", "%s %s" % (flag, flags[flag]), setupText)
372 if subsMade == 0:
373 print "Flag %s wasn't found in setup0.h!" % flag
374 sys.exit(1)
375
376 setupFile = open(os.path.join(mswIncludeDir, "setup.h"), "wb")
377 setupFile.write(setupText)
378 setupFile.close()
379 args = []
380 if toolkit == "msvc":
381 print "setting build options..."
382 args.append("-f makefile.vc")
383 if options.unicode:
384 args.append("UNICODE=1")
385 if VERSION < (2,9):
386 args.append("MSLU=1")
387
388 if options.wxpython:
389 args.append("OFFICIAL_BUILD=1")
390 args.append("SHARED=1")
391 args.append("MONOLITHIC=0")
392 args.append("USE_OPENGL=1")
393 args.append("USE_GDIPLUS=1")
394
395 if not options.debug:
396 args.append("BUILD=release")
397 else:
398 args.append("BUILD=debug")
399
400 if options.shared:
401 args.append("SHARED=1")
402 if options.cairo:
403 args.append("USE_CAIRO=1")
404
405 wxBuilder = builder.MSVCBuilder()
406
407 if toolkit == "msvcProject":
408 args = []
409 if options.shared or options.wxpython:
410 args.append("wx_dll.dsw")
411 else:
412 args.append("wx.dsw")
413
414 # TODO:
415 wxBuilder = builder.MSVCProjectBuilder()
416
417
418 if not wxBuilder:
419 print "Builder not available for your specified platform/compiler."
420 sys.exit(1)
421
422 if options.clean:
423 print "Performing cleanup."
424 wxBuilder.clean(dir=buildDir, options=args)
425
426 sys.exit(0)
427
428 if options.extra_make:
429 args.append(options.extra_make)
430
431 if not sys.platform.startswith("win"):
432 args.append("--jobs=" + options.jobs)
433 exitIfError(wxBuilder.build(dir=buildDir, options=args), "Error building")
434
435 if options.install:
436 extra=None
437 if installDir:
438 extra = ['DESTDIR='+installDir]
439 wxBuilder.install(dir=buildDir, options=extra)
440
441 if options.install and options.mac_framework:
442
443 def renameLibrary(libname, frameworkname):
444 reallib = libname
445 links = []
446 while os.path.islink(reallib):
447 links.append(reallib)
448 reallib = "lib/" + os.readlink(reallib)
449
450 #print "reallib is %s" % reallib
451 run("mv -f %s lib/%s.dylib" % (reallib, frameworkname))
452
453 for link in links:
454 run("ln -s -f %s.dylib %s" % (frameworkname, link))
455
456 frameworkRootDir = prefixDir
457 if installDir:
458 print "installDir = %s" % installDir
459 frameworkRootDir = installDir + prefixDir
460 os.chdir(frameworkRootDir)
461 build_string = ""
462 if options.debug:
463 build_string = "d"
464
465 fwname = getFrameworkName(options)
466 version = commands.getoutput("bin/wx-config --release")
467 version_full = commands.getoutput("bin/wx-config --version")
468 basename = commands.getoutput("bin/wx-config --basename")
469 configname = commands.getoutput("bin/wx-config --selected-config")
470
471 os.makedirs("Resources")
472 wxplist = dict(
473 CFBundleDevelopmentRegion="English",
474 CFBundleIdentifier='org.wxwidgets.wxosxcocoa',
475 CFBundleName=fwname,
476 CFBundleVersion=version_full,
477 CFBundleExecutable=fwname,
478 CFBundleGetInfoString="%s %s" % (fwname, version_full),
479 CFBundlePackageType="FMWK",
480 CFBundleSignature="WXCO",
481 CFBundleShortVersionString=version_full,
482 CFBundleInfoDictionaryVersion="6.0",
483 )
484
485 import plistlib
486 plistlib.writePlist(wxplist, os.path.join(frameworkRootDir, "Resources", "Info.plist"))
487
488 # we make wx the "actual" library file and link to it from libwhatever.dylib
489 # so that things can link to wx and survive minor version changes
490 renameLibrary("lib/lib%s-%s.dylib" % (basename, version), fwname)
491 run("ln -s -f lib/%s.dylib %s" % (fwname, fwname))
492
493 run("ln -s -f include Headers")
494
495 for lib in ["GL", "STC", "Gizmos", "Gizmos_xrc"]:
496 libfile = "lib/lib%s_%s-%s.dylib" % (basename, lib.lower(), version)
497 if os.path.exists(libfile):
498 frameworkDir = "framework/wx%s/%s" % (lib, version)
499 if not os.path.exists(frameworkDir):
500 os.makedirs(frameworkDir)
501 renameLibrary(libfile, "wx" + lib)
502 run("ln -s -f ../../../%s %s/wx%s" % (libfile, frameworkDir, lib))
503
504 for lib in glob.glob("lib/*.dylib"):
505 if not os.path.islink(lib):
506 corelibname = "lib/lib%s-%s.0.dylib" % (basename, version)
507 run("install_name_tool -id %s %s" % (os.path.join(prefixDir, lib), lib))
508 run("install_name_tool -change %s %s %s" % (os.path.join(frameworkRootDir, corelibname), os.path.join(prefixDir, corelibname), lib))
509
510 os.chdir("include")
511
512 header_template = """
513 #ifndef __WX_FRAMEWORK_HEADER__
514 #define __WX_FRAMEWORK_HEADER__
515
516 %s
517
518 #endif // __WX_FRAMEWORK_HEADER__
519 """
520 headers = ""
521 header_dir = "wx-%s/wx" % version
522 for include in glob.glob(header_dir + "/*.h"):
523 headers += "#include <wx/" + os.path.basename(include) + ">\n"
524
525 framework_header = open("%s.h" % fwname, "w")
526 framework_header.write(header_template % headers)
527 framework_header.close()
528
529 run("ln -s -f %s wx" % header_dir)
530 os.chdir("wx-%s/wx" % version)
531 run("ln -s -f ../../../lib/wx/include/%s/wx/setup.h setup.h" % configname)
532
533 os.chdir(os.path.join(frameworkRootDir, ".."))
534 run("ln -s -f %s Current" % getWxRelease())
535 os.chdir("..")
536 run("ln -s -f Versions/Current/Headers Headers")
537 run("ln -s -f Versions/Current/Resources Resources")
538 run("ln -s -f Versions/Current/%s %s" % (fwname, fwname))
539
540 # sanity check to ensure the symlink works
541 os.chdir("Versions/Current")
542
543 # put info about the framework into wx-config
544 os.chdir(frameworkRootDir)
545 text = file('lib/wx/config/%s' % configname).read()
546 text = text.replace("MAC_FRAMEWORK=", "MAC_FRAMEWORK=%s" % getFrameworkName(options))
547 if options.mac_framework_prefix not in ['/Library/Frameworks',
548 '/System/Library/Frameworks']:
549 text = text.replace("MAC_FRAMEWORK_PREFIX=",
550 "MAC_FRAMEWORK_PREFIX=%s" % options.mac_framework_prefix)
551 file('lib/wx/config/%s' % configname, 'w').write(text)
552
553 # The framework is finished!
554 print "wxWidgets framework created at: " + \
555 os.path.join( installDir,
556 options.mac_framework_prefix,
557 '%s.framework' % fwname)
558
559
560 # adjust the install_name if needed
561 if sys.platform.startswith("darwin") and \
562 options.install and \
563 options.installdir and \
564 not options.mac_framework and \
565 not options.wxpython: # wxPython's build will do this later if needed
566 if not prefixDir:
567 prefixDir = '/usr/local'
568 macFixupInstallNames(options.installdir, prefixDir)#, buildDir)
569
570 # make a package if a destdir was set.
571 if options.mac_framework and \
572 options.install and \
573 options.installdir and \
574 options.mac_distdir:
575
576 if os.path.exists(options.mac_distdir):
577 shutil.rmtree(options.mac_distdir)
578
579 packagedir = os.path.join(options.mac_distdir, "packages")
580 os.makedirs(packagedir)
581 basename = os.path.basename(prefixDir.split(".")[0])
582 packageName = basename + "-" + getWxRelease()
583 packageMakerPath = "/Developer/usr/bin/packagemaker "
584 args = []
585 args.append("--root %s" % options.installdir)
586 args.append("--id org.wxwidgets.%s" % basename.lower())
587 args.append("--title %s" % packageName)
588 args.append("--version %s" % getWxRelease())
589 args.append("--out %s" % os.path.join(packagedir, packageName + ".pkg"))
590 cmd = packageMakerPath + ' '.join(args)
591 print "cmd = %s" % cmd
592 run(cmd)
593
594 os.chdir(options.mac_distdir)
595
596 run('hdiutil create -srcfolder %s -volname "%s" -imagekey zlib-level=9 %s.dmg' % (packagedir, packageName, packageName))
597
598 shutil.rmtree(packagedir)
599
600 if __name__ == '__main__':
601 exitWithException = False # use sys.exit instead
602 main(sys.argv[0], sys.argv[1:])
603