#! /usr/bin/env python #----------------------------------------------------------------------------- # Copyright (c) 2013, PyInstaller Development Team. # # Distributed under the terms of the GNU General Public License with exception # for distributing bootloader. # # The full license is in the file COPYING.txt, distributed with this software. #----------------------------------------------------------------------------- """ Bootloader building script. """ import os import platform import sys import Utils import Options # the following two variables are used by the target "waf dist" VERSION='' APPNAME='' # these variables are mandatory ('/' are converted automatically) top = '.' out = 'build' # TODO use following variable only on darwin and for 32bit loader. # OS X 10.5 doesn't understand the load command 'LC_DYLD_INFO_ONLY' # that is used by the OS X 10.6 linker. # The following variable fixes 10.5 compatibility. os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.5' # TODO strip created binaries. def set_options(opt): myplatform = Utils.detect_platform() if not myplatform.startswith('win'): opt.add_option('--leak-detector', action = 'store_true', help = 'Link with Boehm garbage collector to detect memory leaks.', default = False, dest = 'boehmgc') if myplatform.startswith('linux'): opt.add_option('--no-lsb', action = 'store_true', help = 'Prevent building LSB (Linux Standard Base) bootloader.', default = False, dest = 'nolsb') opt.add_option('--lsbcc-path', action = 'store', help = 'Path to lsbcc. By default PATH is searched for lsbcc otherwise is tried file /opt/lsb/bin/lsbcc. [Default: lsbcc]', default = 'lsbcc', dest = 'lsbcc_path') opt.add_option('--lsb-target-version', action = 'store', help = 'Specify LSB target version [Default: 4.0]', default = '4.0', dest = 'lsb_version') opt.tool_options('compiler_cc') def configure(conf): def achitecture(): return '32bit' conf.env.NAME = 'default' opt = Options.options myplatform = Utils.detect_platform() # on 64bit Mac function platform.architecture() returns 64bit even # for 32bit Python. This is the workaround for this. if myplatform == 'darwin' and sys.maxint <= 3**32: myarch = '32bit' else: myarch = platform.architecture()[0] # 32bit or 64bit conf.env.MYARCH = myarch # Differenciate path to bootloader with machine name if necessary. mymach = platform.machine() if mymach.startswith('arm'): mymach = 'arm' else: # Assume x86/x86_64 machine. mymach = None conf.env.MYMACHINE = mymach Utils.pprint('CYAN', '%s-%s detected' % (platform.system(), myarch)) if myplatform == 'darwin' and myarch == '64bit': Utils.pprint('CYAN', 'WARNING: Building bootloader for Python 64-bit on Mac OSX') Utils.pprint('CYAN', 'For 32b-bit bootloader prepend the python command with:') Utils.pprint('CYAN', 'VERSIONER_PYTHON_PREFER_32_BIT=yes arch -i386 python') if myplatform.startswith('linux') and not opt.nolsb: Utils.pprint('CYAN', 'Building LSB bootloader.') ### C compiler if myplatform.startswith('win'): try: # mingw (gcc for Windows) conf.check_tool('gcc') except: try: # Newest detected MSVC version is used by default. # msvc 7.1 (Visual Studio 2003) # msvc 8.0 (Visual Studio 2005) # msvc 9.0 (Visual Studio 2008) conf.env['MSVC_VERSIONS'] = ['msvc 7.1', 'msvc 8.0', 'msvc 9.0', 'wsdk 7.0', 'wsdk 7.1'] if myarch == '32bit': conf.env['MSVC_TARGETS'] = ['x86', 'ia32'] elif myarch == '64bit': conf.env['MSVC_TARGETS'] = ['x64', 'x86_amd64', 'intel64', 'em64t'] conf.check_tool('msvc') conf.print_all_msvc_detected() # Do not embed manifest file. # Manifest file will be added in the phase of packaging python application. conf.env.MSVC_MANIFEST = False except Exception, e: print e Utils.pprint('RED', str(e)) Utils.pprint('RED', 'GCC (MinGW) or MSVC compiler not found.') exit(1) else: # When using GCC, deactivate declarations after statements # (not supported by Visual Studio) conf.env.append_value('CCFLAGS', '-Wdeclaration-after-statement') conf.env.append_value('CCFLAGS', '-Werror') else: conf.check_tool('compiler_cc') ### build LSB (Linux Standard Base) boot loader if myplatform.startswith('linux') and not opt.nolsb: try: # custom lsbcc path if opt.lsbcc_path != 'lsbcc': conf.env.LSBCC = conf.find_program(opt.lsbcc_path, mandatory=True) # default values else: conf.env.LSBCC = conf.find_program(opt.lsbcc_path) if not conf.env.LSBCC: conf.env.LSBCC = conf.find_program('/opt/lsb/bin/lsbcc', mandatory=True) except: Utils.pprint('RED', 'LSB (Linux Standard Base) tools >= 4.0 are required.') Utils.pprint('RED', 'Try --no-lsb option if not interested in building LSB binary.') exit(2) # lsbcc as CC compiler conf.env.append_value('CCFLAGS', '--lsb-cc=%s' % conf.env.CC[0]) conf.env.append_value('LINKFLAGS', '--lsb-cc=%s' % conf.env.CC[0]) conf.env.CC = conf.env.LSBCC conf.env.LINK_CC = conf.env.LSBCC ## check LSBCC flags # --lsb-besteffort - binary will work on platforms without LSB stuff # --lsb-besteffort - available in LSB build tools >= 4.0 conf.check_cc(ccflags='--lsb-besteffort', msg='Checking for LSB build tools >= 4.0', errmsg='LSB >= 4.0 is required', mandatory=True) conf.env.append_value('CCFLAGS', '--lsb-besteffort') conf.env.append_value('LINKFLAGS', '--lsb-besteffort') # binary compatibility with a specific LSB version # LSB 4.0 can generate binaries compatible with 3.0, 3.1, 3.2, 4.0 # however because of using function 'mkdtemp', loader requires # using target version 4.0 lsb_target_flag = '--lsb-target-version=%s' % opt.lsb_version conf.env.append_value('CCFLAGS', lsb_target_flag) conf.env.append_value('LINKFLAGS', lsb_target_flag) ### Defines, Includes if myplatform.startswith('lin'): # make sure we don't use declarations after statement (break Visual Studio) conf.env.append_value('CCFLAGS', '-Wdeclaration-after-statement') conf.env.append_value('CCFLAGS', '-Werror') if not myplatform.startswith('win'): # Defines common for Unix and Unix-like platforms. # For details see: # http://man.he.net/man7/feature_test_macros # ## Without these definitions compiling maight fail on OSX. conf.env.append_value('CCDEFINES', '_POSIX_C_SOURCE=200112L') # SUS v2 (UNIX 98) definitions. # Mac OS X 10.5 is UNIX 03 compliant. conf.env.append_value('CCDEFINES', '_XOPEN_SOURCE=500') conf.env.append_value('CCDEFINES', '_REENTRANT') # Function 'mkdtemp' is available only if _BSD_SOURCE is defined. conf.env.append_value('CCDEFINES', '_BSD_SOURCE') if myplatform.startswith('win'): conf.env.append_value('CCDEFINES', 'WIN32') conf.env.append_value('CPPPATH', '../zlib') if myplatform.startswith('sun'): conf.env.append_value('CCDEFINES', 'SUNOS') if myplatform.startswith('aix'): conf.env.append_value('CCDEFINES', 'AIX') conf.env.append_value('CPPPATH', os.path.join('..', 'common')) ### Libraries if myplatform.startswith('win'): conf.check_cc(lib='user32', mandatory=True) conf.check_cc(lib='comctl32', mandatory=True) conf.check_cc(lib='kernel32', mandatory=True) conf.check_cc(lib='ws2_32', mandatory=True) else: conf.check_cc(lib='dl', mandatory=True) conf.check_cc(lib='z', mandatory=True) if conf.check_cc(function_name='readlink', header_name='unistd.h'): conf.env.append_value('CCFLAGS', '-DHAVE_READLINK') # This uses Boehm GC to manage memory - it replaces malloc() / free() # functions. Some messages are printed if memory is not deallocated. if opt.boehmgc: conf.check_cc(lib='gc', mandatory=True) conf.env.append_value('CCDEFINES', 'PYI_LEAK_DETECTOR') conf.env.append_value('CCDEFINES', 'GC_FIND_LEAK') conf.env.append_value('CCDEFINES', 'GC_DEBUG') conf.env.append_value('CCDEFINES', 'SAVE_CALL_CHAIN') ### ccflags if myplatform.startswith('win') and conf.env.CC_NAME == 'gcc': # Disables console - MinGW option conf.check_cc(ccflags='-mwindows', mandatory=True, msg='Checking for flags -mwindows') # Use Visual C++ compatible alignment conf.check_cc(ccflags='-mms-bitfields', mandatory=True, msg='Checking for flags -mms-bitfields') conf.env.append_value('CCFLAGS', '-mms-bitfields') elif myplatform.startswith('win') and conf.env.CC_NAME == 'msvc': if myarch == '32bit': conf.env.append_value('LINKFLAGS', '/MACHINE:X86') elif myarch == '64bit': conf.env.append_value('LINKFLAGS', '/MACHINE:X64') # Enable 64bit porting warnings and other warnings too. conf.env.append_value('CCFLAGS', '/W3') # We use SEH exceptions in winmain.c; make sure they are activated. conf.env.append_value('CCFLAGS', '/EHa') # Compile with 64bit gcc 32bit binaries or vice versa. if conf.env.CC_NAME == 'gcc': if myarch == '32bit' and conf.check_cc(ccflags='-m32', msg='Checking for flags -m32'): conf.env.append_value('CCFLAGS', '-m32') elif myarch == '64bit': conf.env.append_value('CCFLAGS', '-m64') # Ensure proper architecture flags on Mac OS X. # TODO Add support for universal binaries. if myplatform.startswith('darwin'): available_archs = {'32bit': 'i386', '64bit': 'x86_64'} mac_arch = available_archs[myarch] conf.env.append_value('CCFLAGS', '-arch') conf.env.append_value('CCFLAGS', mac_arch) conf.env.append_value('CXXFLAGS', '-arch') conf.env.append_value('CXXFLAGS', mac_arch) conf.env.append_value('LINKFLAGS', '-arch') conf.env.append_value('LINKFLAGS', mac_arch) if myarch == '32bit': conf.env.append_value('CCFLAGS', '-mmacosx-version-min=10.5') else: conf.env.append_value('CCFLAGS', '-mmacosx-version-min=10.6') # On linux link only with needed libraries. # -Wl,--as-needed is on some platforms detected during configure but # fails during build. (Mac OS X, Solaris, AIX) if myplatform.startswith('linux') and conf.check_cc(ccflags='-Wl,--as-needed', msg='Checking for flags -Wl,--as-needed'): conf.env.append_value('LINKFLAGS', '-Wl,--as-needed') ### Other stuff if myplatform.startswith('win'): # RC file - icon conf.check_tool('winres') ### DEBUG and RELEASE environments rel = conf.env.copy() dbg = conf.env.copy() rel.set_variant('release') # separate subfolder for building dbg.set_variant('debug') # separate subfolder for building rel.detach() # detach environment from default dbg.detach() ## setup DEBUG environment dbg.set_variant('debug') # separate subfolder for building conf.set_env_name('debug', dbg) conf.setenv('debug') # This define enables verbose console output of the bootloader. conf.env.append_value('CCDEFINES', ['LAUNCH_DEBUG']) conf.env.append_value('CCDEFINES', 'NDEBUG') dbgw = conf.env.copy() # WINDOWED DEBUG environment dbgw.set_variant('debugw') # separate subfolder for building dbgw.detach() ## setup windowed DEBUG environment conf.set_env_name('debugw', dbgw) conf.setenv('debugw') conf.env.append_value('CCDEFINES', 'WINDOWED') # disables console - mingw option if myplatform.startswith('win') and conf.env.CC_NAME == 'gcc': conf.env.append_value('LINKFLAGS', '-mwindows') elif myplatform.startswith('darwin'): #conf.env.append_value('CCFLAGS', '-I/Developer/Headers/FlatCarbon') # To support catching AppleEvents and running as ordinary OSX GUI app, # we have to link against the Carbon framework. # This linkage only needs to be there for the windowed bootloaders. conf.env.append_value('LINKFLAGS', '-framework') conf.env.append_value('LINKFLAGS', 'Carbon') # conf.env.append_value('LINKFLAGS', '-framework') # conf.env.append_value('LINKFLAGS', 'ApplicationServices') ## setup RELEASE environment conf.set_env_name('release', rel) conf.setenv('release') conf.env.append_value('CCDEFINES', 'NDEBUG') conf.env.append_value('CCFLAGS', conf.env.CCFLAGS_RELEASE) relw = conf.env.copy() # WINDOWED RELEASE environment relw.set_variant('releasew') # separate subfolder for building relw.detach() ## setup windowed RELEASE environment conf.set_env_name('releasew', relw) conf.setenv('releasew') conf.env.append_value('CCDEFINES', 'WINDOWED') # disables console if myplatform.startswith('win') and conf.env.CC_NAME == 'gcc': conf.env.append_value('LINKFLAGS', '-mwindows') elif myplatform.startswith('darwin'): # To support catching AppleEvents and running as ordinary OSX GUI app, # we have to link against the Carbon framework. # This linkage only needs to be there for the windowed bootloaders. conf.env.append_value('LINKFLAGS', '-framework') conf.env.append_value('LINKFLAGS', 'Carbon') # TODO Do we need to link with this framework? # conf.env.append_value('LINKFLAGS', '-framework') # conf.env.append_value('LINKFLAGS', 'ApplicationServices') # TODO Use 'strip' command to decrease the size of compiled bootloaders. def build(bld): myplatform = Utils.detect_platform() opt = Options.options # Force to run with 1 job (no parallel building). # There are reported build failures on multicore CPUs # with some msvc versions. # TODO revisit parallel building with switch to waf 1.7 opt.jobs = 1 install_path = '../../PyInstaller/bootloader/' + platform.system() + "-" + bld.env.MYARCH if bld.env.MYMACHINE: install_path += '-' + bld.env.MYMACHINE targets = dict(release='run', debug='run_d', releasew='runw', debugw='runw_d') if myplatform.startswith('win'): # static lib zlib for key in targets.keys(): bld( features = 'cc cstaticlib', source = bld.path.ant_glob('zlib/*.c'), target = 'staticlib_zlib', env = bld.env_of_name(key), includes = ['zlib'], ) # console for key in ('release', 'debug'): bld( features = 'cc cprogram', source = bld.path.ant_glob('windows/utils.c windows/run.rc common/*.c'), target = targets[key], install_path = install_path, uselib_local = 'staticlib_zlib', uselib = 'USER32 COMCTL32 KERNEL32 WS2_32', env = bld.env_of_name(key).copy(), includes = ['common', 'windows', 'zlib'], ) # windowed for key in ('releasew', 'debugw'): bld( features = 'cc cprogram', source = bld.path.ant_glob('windows/utils.c windows/runw.rc common/*.c'), # uses different RC file (icon) target = targets[key], install_path = install_path, uselib_local = 'staticlib_zlib', uselib = 'USER32 COMCTL32 KERNEL32 WS2_32', env = bld.env_of_name(key).copy(), includes = ['common', 'windows', 'zlib'], ) ## inprocsrvr console if bld.env.CC_NAME == 'msvc': linkflags_c = bld.env.CPPFLAGS_CONSOLE linkflags_w = bld.env.CPPFLAGS_WINDOWS else: linkflags_c = '' linkflags_w = '' for key, value in dict(release='inprocsrvr', debug='inprocsrvr_d').items(): bld( features = 'cc cshlib', source = bld.path.ant_glob('common/pyi_*.c windows/*.c'), target = value, install_path = install_path, uselib_local = 'staticlib_zlib', uselib = 'USER32 COMCTL32 KERNEL32 WS2_32', env = bld.env_of_name(key).copy(), linkflags = linkflags_c, includes = ['common', 'windows', 'zlib'], ) ## inprocsrvr windowed for key, value in dict(releasew='inprocsrvrw', debugw='inprocsrvrw_d').items(): bld( features = 'cc cshlib', source = bld.path.ant_glob('common/pyi_*.c windows/*.c'), target = value, install_path = install_path, uselib_local = 'staticlib_zlib', uselib = 'USER32 COMCTL32 KERNEL32 WS2_32', env = bld.env_of_name(key).copy(), linkflags = linkflags_w, includes = ['common', 'windows', 'zlib'], ) else: # linux, darwin (MacOSX) libs = ['dl', 'z', 'm'] # 'z' - zlib, 'm' - math, if opt.boehmgc: libs.append('gc') for key, value in targets.items(): bld( features = 'cc cprogram', source = bld.path.ant_glob('linux/*.c common/*.c'), target = value, install_path = install_path, lib = libs, env = bld.env_of_name(key).copy(), includes = ['common', 'linux'], )