Merge in from trunk r68684 - r69046
[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 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 if not os.environ.get("CAIRO_ROOT"):
347 print "WARNING: Expected CAIRO_ROOT set in the environment!"
348 flags["wxUSE_CAIRO"] = "1"
349
350 if options.wxpython:
351 flags["wxDIALOG_UNIT_COMPATIBILITY "] = "0"
352 flags["wxUSE_DEBUGREPORT"] = "0"
353 flags["wxUSE_DIALUP_MANAGER"] = "0"
354 flags["wxUSE_GRAPHICS_CONTEXT"] = "1"
355 flags["wxUSE_DISPLAY"] = "1"
356 flags["wxUSE_GLCANVAS"] = "1"
357 flags["wxUSE_POSTSCRIPT"] = "1"
358 flags["wxUSE_AFM_FOR_POSTSCRIPT"] = "0"
359 flags["wxUSE_DATEPICKCTRL_GENERIC"] = "1"
360
361 if VERSION < (2,9):
362 flags["wxUSE_DIB_FOR_BITMAP"] = "1"
363
364 if VERSION >= (2,9):
365 flags["wxUSE_UIACTIONSIMULATOR"] = "1"
366
367
368 mswIncludeDir = os.path.join(wxRootDir, "include", "wx", "msw")
369 setup0File = os.path.join(mswIncludeDir, "setup0.h")
370 setupText = open(setup0File, "rb").read()
371
372 for flag in flags:
373 setupText, subsMade = re.subn(flag + "\s+?\d", "%s %s" % (flag, flags[flag]), setupText)
374 if subsMade == 0:
375 print "Flag %s wasn't found in setup0.h!" % flag
376 sys.exit(1)
377
378 setupFile = open(os.path.join(mswIncludeDir, "setup.h"), "wb")
379 setupFile.write(setupText)
380 setupFile.close()
381 args = []
382 if toolkit == "msvc":
383 print "setting build options..."
384 args.append("-f makefile.vc")
385 if options.unicode:
386 args.append("UNICODE=1")
387 if VERSION < (2,9):
388 args.append("MSLU=1")
389
390 if options.wxpython:
391 args.append("OFFICIAL_BUILD=1")
392 args.append("SHARED=1")
393 args.append("MONOLITHIC=0")
394 args.append("USE_OPENGL=1")
395 args.append("USE_GDIPLUS=1")
396
397 if not options.debug:
398 args.append("BUILD=release")
399 else:
400 args.append("BUILD=debug")
401
402 if options.shared:
403 args.append("SHARED=1")
404
405 if options.cairo:
406 args.append(
407 "CPPFLAGS=/I%s" %
408 os.path.join(os.environ.get("CAIRO_ROOT", ""), 'include\\cairo'))
409
410 wxBuilder = builder.MSVCBuilder()
411
412 if toolkit == "msvcProject":
413 args = []
414 if options.shared or options.wxpython:
415 args.append("wx_dll.dsw")
416 else:
417 args.append("wx.dsw")
418
419 # TODO:
420 wxBuilder = builder.MSVCProjectBuilder()
421
422
423 if not wxBuilder:
424 print "Builder not available for your specified platform/compiler."
425 sys.exit(1)
426
427 if options.clean:
428 print "Performing cleanup."
429 wxBuilder.clean(dir=buildDir, options=args)
430
431 sys.exit(0)
432
433 if options.extra_make:
434 args.append(options.extra_make)
435
436 if not sys.platform.startswith("win"):
437 args.append("--jobs=" + options.jobs)
438 exitIfError(wxBuilder.build(dir=buildDir, options=args), "Error building")
439
440 if options.install:
441 extra=None
442 if installDir:
443 extra = ['DESTDIR='+installDir]
444 wxBuilder.install(dir=buildDir, options=extra)
445
446 if options.install and options.mac_framework:
447
448 def renameLibrary(libname, frameworkname):
449 reallib = libname
450 links = []
451 while os.path.islink(reallib):
452 links.append(reallib)
453 reallib = "lib/" + os.readlink(reallib)
454
455 #print "reallib is %s" % reallib
456 run("mv -f %s lib/%s.dylib" % (reallib, frameworkname))
457
458 for link in links:
459 run("ln -s -f %s.dylib %s" % (frameworkname, link))
460
461 frameworkRootDir = prefixDir
462 if installDir:
463 print "installDir = %s" % installDir
464 frameworkRootDir = installDir + prefixDir
465 os.chdir(frameworkRootDir)
466 build_string = ""
467 if options.debug:
468 build_string = "d"
469
470 fwname = getFrameworkName(options)
471 version = commands.getoutput("bin/wx-config --release")
472 version_full = commands.getoutput("bin/wx-config --version")
473 basename = commands.getoutput("bin/wx-config --basename")
474 configname = commands.getoutput("bin/wx-config --selected-config")
475
476 os.makedirs("Resources")
477 wxplist = dict(
478 CFBundleDevelopmentRegion="English",
479 CFBundleIdentifier='org.wxwidgets.wxosxcocoa',
480 CFBundleName=fwname,
481 CFBundleVersion=version_full,
482 CFBundleExecutable=fwname,
483 CFBundleGetInfoString="%s %s" % (fwname, version_full),
484 CFBundlePackageType="FMWK",
485 CFBundleSignature="WXCO",
486 CFBundleShortVersionString=version_full,
487 CFBundleInfoDictionaryVersion="6.0",
488 )
489
490 import plistlib
491 plistlib.writePlist(wxplist, os.path.join(frameworkRootDir, "Resources", "Info.plist"))
492
493 # we make wx the "actual" library file and link to it from libwhatever.dylib
494 # so that things can link to wx and survive minor version changes
495 renameLibrary("lib/lib%s-%s.dylib" % (basename, version), fwname)
496 run("ln -s -f lib/%s.dylib %s" % (fwname, fwname))
497
498 run("ln -s -f include Headers")
499
500 for lib in ["GL", "STC", "Gizmos", "Gizmos_xrc"]:
501 libfile = "lib/lib%s_%s-%s.dylib" % (basename, lib.lower(), version)
502 if os.path.exists(libfile):
503 frameworkDir = "framework/wx%s/%s" % (lib, version)
504 if not os.path.exists(frameworkDir):
505 os.makedirs(frameworkDir)
506 renameLibrary(libfile, "wx" + lib)
507 run("ln -s -f ../../../%s %s/wx%s" % (libfile, frameworkDir, lib))
508
509 for lib in glob.glob("lib/*.dylib"):
510 if not os.path.islink(lib):
511 corelibname = "lib/lib%s-%s.0.dylib" % (basename, version)
512 run("install_name_tool -id %s %s" % (os.path.join(prefixDir, lib), lib))
513 run("install_name_tool -change %s %s %s" % (os.path.join(frameworkRootDir, corelibname), os.path.join(prefixDir, corelibname), lib))
514
515 os.chdir("include")
516
517 header_template = """
518 #ifndef __WX_FRAMEWORK_HEADER__
519 #define __WX_FRAMEWORK_HEADER__
520
521 %s
522
523 #endif // __WX_FRAMEWORK_HEADER__
524 """
525 headers = ""
526 header_dir = "wx-%s/wx" % version
527 for include in glob.glob(header_dir + "/*.h"):
528 headers += "#include <wx/" + os.path.basename(include) + ">\n"
529
530 framework_header = open("%s.h" % fwname, "w")
531 framework_header.write(header_template % headers)
532 framework_header.close()
533
534 run("ln -s -f %s wx" % header_dir)
535 os.chdir("wx-%s/wx" % version)
536 run("ln -s -f ../../../lib/wx/include/%s/wx/setup.h setup.h" % configname)
537
538 os.chdir(os.path.join(frameworkRootDir, ".."))
539 run("ln -s -f %s Current" % getWxRelease())
540 os.chdir("..")
541 run("ln -s -f Versions/Current/Headers Headers")
542 run("ln -s -f Versions/Current/Resources Resources")
543 run("ln -s -f Versions/Current/%s %s" % (fwname, fwname))
544
545 # sanity check to ensure the symlink works
546 os.chdir("Versions/Current")
547
548 # put info about the framework into wx-config
549 os.chdir(frameworkRootDir)
550 text = file('lib/wx/config/%s' % configname).read()
551 text = text.replace("MAC_FRAMEWORK=", "MAC_FRAMEWORK=%s" % getFrameworkName(options))
552 if options.mac_framework_prefix not in ['/Library/Frameworks',
553 '/System/Library/Frameworks']:
554 text = text.replace("MAC_FRAMEWORK_PREFIX=",
555 "MAC_FRAMEWORK_PREFIX=%s" % options.mac_framework_prefix)
556 file('lib/wx/config/%s' % configname, 'w').write(text)
557
558 # The framework is finished!
559 print "wxWidgets framework created at: " + \
560 os.path.join( installDir,
561 options.mac_framework_prefix,
562 '%s.framework' % fwname)
563
564
565 # adjust the install_name if needed
566 if sys.platform.startswith("darwin") and \
567 options.install and \
568 options.installdir and \
569 not options.mac_framework and \
570 not options.wxpython: # wxPython's build will do this later if needed
571 if not prefixDir:
572 prefixDir = '/usr/local'
573 macFixupInstallNames(options.installdir, prefixDir)#, buildDir)
574
575 # make a package if a destdir was set.
576 if options.mac_framework and \
577 options.install and \
578 options.installdir and \
579 options.mac_distdir:
580
581 if os.path.exists(options.mac_distdir):
582 shutil.rmtree(options.mac_distdir)
583
584 packagedir = os.path.join(options.mac_distdir, "packages")
585 os.makedirs(packagedir)
586 basename = os.path.basename(prefixDir.split(".")[0])
587 packageName = basename + "-" + getWxRelease()
588 packageMakerPath = "/Developer/usr/bin/packagemaker "
589 args = []
590 args.append("--root %s" % options.installdir)
591 args.append("--id org.wxwidgets.%s" % basename.lower())
592 args.append("--title %s" % packageName)
593 args.append("--version %s" % getWxRelease())
594 args.append("--out %s" % os.path.join(packagedir, packageName + ".pkg"))
595 cmd = packageMakerPath + ' '.join(args)
596 print "cmd = %s" % cmd
597 run(cmd)
598
599 os.chdir(options.mac_distdir)
600
601 run('hdiutil create -srcfolder %s -volname "%s" -imagekey zlib-level=9 %s.dmg' % (packagedir, packageName, packageName))
602
603 shutil.rmtree(packagedir)
604
605 if __name__ == '__main__':
606 exitWithException = False # use sys.exit instead
607 main(sys.argv[0], sys.argv[1:])
608