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