#!/usr/bin/env python

###################################
# Author: Kevin Ollivier
# License: wxWidgets License
###################################

import os
import re
import sys
import builder
import commands
import glob
import optparse
import platform
import shutil
import types

# builder object
wxBuilder = None

scriptDir = os.path.abspath(sys.path[0])
wxRootDir = os.path.abspath(os.path.join(scriptDir, "..", ".."))
contribDir = os.path.join("contrib", "src")
installDir = None

if sys.platform.startswith("win"):
    contribDir = os.path.join(wxRootDir, "contrib", "build")

if sys.platform.startswith("win"):
    toolkit = "msvc"
else:
    toolkit = "autoconf"

option_dict = { 
            "clean"     : (False, "Clean all files from the build directory"),
            "debug"     : (False, "Build the library in debug symbols"),
            "install"   : (False, "Install the toolkit to the installdir directory, or the default dir."),
            "installdir" : (".", "Directory where built wxWidgets will be installed"),
            "mac_universal_binary" : (False, "Build Mac version as a universal binary"),
            "mac_lipo" : (False, "EXPERIMENTAL: Create a universal binary by merging a PPC and Intel build together."),
            "mac_framework" : (False, "Install the Mac build as a framework"),
            "no_config" : (False, "Turn off configure step on autoconf builds"),
            "rebake"    : (False, "Regenerate Bakefile and autoconf files"),
            "unicode"   : (False, "Build the library with unicode support"),
            "wxpython"  : (False, "Build the wxWidgets library with all options needed by wxPython"),
            "cocoa"     : (False, "Build the Cooca port (Mac only currently)."),
            "osx_cocoa" : (False, "Build the new Cocoa port"),
            "shared"    : (False, "Build wx as a dynamic library"),
            "cairo"     : (False, "Build support for wxCairoContext (always true on GTK+)"),
          }
    
parser = optparse.OptionParser(usage="usage: %prog [options]", version="%prog 1.0")

for opt in option_dict:
    default = option_dict[opt][0]
    
    action = "store"
    if type(default) == types.BooleanType:
        action = "store_true"
    parser.add_option("--" + opt, default=default, action=action, dest=opt, help=option_dict[opt][1])

options, arguments = parser.parse_args()

def exitIfError(code, msg):
    if code != 0:
        print msg
        sys.exit(1)
        
def getWxRelease():
    global wxRootDir
    configureText = open(os.path.join(wxRootDir, "configure.in"), "r").read()
    
    majorVersion = re.search("wx_major_version_number=(\d)", configureText).group(1)
    minorVersion = re.search("wx_minor_version_number=(\d)", configureText).group(1)
    
    return "%s.%s" % (majorVersion, minorVersion)
        
def doMacLipoBuild(arch, buildDir, installDir, cxxcompiler="g++-4.0", cccompiler="gcc-4.0", target="10.4", flags=""):
    # PPC build
    archInstallDir = installDir + "/" + arch
    os.environ["CXX"] = "%s -arch %s %s" % (cxxcompiler, arch, flags)
    os.environ["CC"] = "%s -arch %s %s" % (cccompiler, arch, flags)
    os.environ["MACOSX_DEPLOYMENT_TARGET"] = target
    archArgs = ["prefix=" + archInstallDir]
    buildRoot = "bld-" + arch
    if buildDir:
        buildRoot = buildDir + "/" + buildRoot
    
    if not os.path.exists(buildRoot):
        os.makedirs(buildRoot)
    
    olddir = os.getcwd()
    os.chdir(buildRoot)
    
    if not options.no_config:
        exitIfError(wxBuilder.configure(options=configure_opts), "Error running configure")
    exitIfError(wxBuilder.build(options=archArgs), "Error building")
    exitIfError(wxBuilder.install(options=["prefix=" + archInstallDir]), "Error Installing")
    
    if options.wxpython and os.path.exists(os.path.join(wxRootDir, contribDir)):
        exitIfError(wxBuilder.build(dir=os.path.join(contribDir, "gizmos"), options=archArgs), "Error building gizmos")
        exitIfError(wxBuilder.install(os.path.join(contribDir, "gizmos"), options=["prefix=" + archInstallDir]), "Error Installing gizmos")
        
        exitIfError(wxBuilder.build(dir=os.path.join(contribDir, "stc"),options=archArgs), "Error building stc")
        exitIfError(wxBuilder.install(os.path.join(contribDir, "stc"),options=["prefix=" + archInstallDir]), "Error installing stc")

    os.chdir(olddir)
    
# compiler / build system specific args
buildDir = None
args = None
installDir = options.installdir

if toolkit == "autoconf":
    configure_opts = []
    
    if options.unicode:
        configure_opts.append("--enable-unicode")
        
    if options.debug:
        configure_opts.append("--enable-debug")
        
    if options.mac_universal_binary: 
        configure_opts.append("--enable-universal_binary")
        
    if options.cocoa:
        configure_opts.append("--with-cocoa")
        
    if options.osx_cocoa:
        configure_opts.append("--with-osx_cocoa")
    
    wxpy_configure_opts = [
                        "--with-opengl",
                        "--enable-sound",
                        "--with-sdl",
                        "--with-gnomeprint",
                        "--enable-graphics_ctx",
                        "--enable-mediactrl",
                        "--enable-display",
                        "--enable-geometry",
                        "--enable-debug_flag",
                        "--enable-optimise",
                        "--disable-debugreport",
                        "--enable-monolithic"
                        ]
                        
    if not options.mac_framework and options.installdir != option_dict["installdir"][0]:
        configure_opts.append("--prefix=" + installDir)

    if options.wxpython:
        configure_opts.extend(wxpy_configure_opts)

    if options.rebake:
        retval = os.system("make -f autogen.mk")
        exitIfError(retval, "Error running autogen.mk")
        
    if options.mac_framework:
        # Framework build is always a universal binary
        options.mac_lipo = True
        name = "wx"
        if options.osx_cocoa:
            name += "OSXCocoa"
        installDir = "/Library/Frameworks/%s.framework/Versions/%s" % (name, getWxRelease())
        configure_opts.append("--prefix=" + installDir)
        # framework builds always need to be monolithic
        if not "--enable-monolithic" in configure_opts:
            configure_opts.append("--enable-monolithic")
        
    print "Configure options: " + `configure_opts`
    wxBuilder = builder.AutoconfBuilder()
    if not options.no_config and not options.clean and not options.mac_lipo:
        exitIfError(wxBuilder.configure(options=configure_opts), "Error running configure")

elif toolkit in ["msvc", "msvcProject"]:
    flags = {}
    buildDir = os.path.join(scriptDir, "..", "msw")

    if options.unicode:
        flags["wxUSE_UNICODE"] = "1"
        flags["wxUSE_UNICODE_MSLU"] = "1"

    if options.cairo:
        flags["wxUSE_CAIRO"] = "1"

    if options.wxpython:
        flags["wxDIALOG_UNIT_COMPATIBILITY "] = "0"
        flags["wxUSE_DEBUG_CONTEXT"] = "1"
        flags["wxUSE_MEMORY_TRACING"] = "1"
        flags["wxUSE_DIALUP_MANAGER"] = "0"
        flags["wxUSE_GLCANVAS"] = "1"
        flags["wxUSE_POSTSCRIPT"] = "1"
        flags["wxUSE_AFM_FOR_POSTSCRIPT"] = "0"
        flags["wxUSE_DISPLAY"] = "1"
        flags["wxUSE_DIB_FOR_BITMAP"] = "1"
        flags["wxUSE_DEBUGREPORT"] = "0"
        flags["wxUSE_GRAPHICS_CONTEXT"] = "1"
        flags["wxUSE_DATEPICKCTRL_GENERIC"] = "1"
    
        # setup the wxPython 'hybrid' build
        if not options.debug:
            flags["wxUSE_MEMORY_TRACING"] = "0"
            flags["wxUSE_DEBUG_CONTEXT"] = "0"

    mswIncludeDir = os.path.join(wxRootDir, "include", "wx", "msw")
    setup0File = os.path.join(mswIncludeDir, "setup0.h")
    setupText = open(setup0File, "rb").read()
    
    for flag in flags:
        setupText, subsMade = re.subn(flag + "\s+?\d", "%s %s" % (flag, flags[flag]), setupText)
        if subsMade == 0:
            print "Flag %s wasn't found!" % flag
            sys.exit(1)

    setupFile = open(os.path.join(mswIncludeDir, "setup.h"), "wb")
    setupFile.write(setupText)
    setupFile.close()
    args = []
    if toolkit == "msvc":
        print "setting build options..."
        args.append("-f makefile.vc")
        if options.unicode:
            args.append("UNICODE=1")
            args.append("MSLU=1")

        if options.wxpython:
            args.append("OFFICIAL_BUILD=1")
            args.append("SHARED=1")
            args.append("MONOLITHIC=0")
            args.append("USE_OPENGL=1")
            args.append("USE_GDIPLUS=1")
            
            if not options.debug:
                args.append("DEBUG_FLAG=1")
                args.append("CXXFLAGS=/D__NO_VC_CRTDBG__")
                args.append("WXDEBUGFLAG=h")
                args.append("BUILD=release")

        wxBuilder = builder.MSVCBuilder()
        
    if toolkit == "msvcProject":
        args = []
        if options.shared or options.wxpython:
            args.append("wx_dll.dsw")
        else:
            args.append("wx.dsw")
            
        # TODO:
        wxBuilder = builder.MSVCProjectBuilder()
    
if not wxBuilder:
    print "Builder not available for your specified platform/compiler."
    sys.exit(1)

if options.clean:
    print "Performing cleanup."
    wxBuilder.clean()
    
    if options.wxpython:
        exitIfError(wxBuilder.clean(os.path.join(contribDir, "gizmos")), "Error building gizmos")
        exitIfError(wxBuilder.clean(os.path.join(contribDir, "stc")), "Error building stc")
    
    sys.exit(0)

isLipo = False    
if options.mac_lipo:
    if options.mac_universal_binary:
        print "WARNING: Cannot specify both mac_lipo and mac_universal_binary, as they conflict."
        print "         Using mac_universal_binary..."
    else:
        isLipo = True
        # TODO: Add 64-bit when we're building OS X Cocoa
        
        # 2.8, use gcc 3.3 on PPC for 10.3 support...
        macVersion = platform.mac_ver()[0]
        isLeopard = macVersion.find("10.5") != -1
        
        if not isLeopard and os.path.exists(os.path.join(wxRootDir, contribDir)):
            doMacLipoBuild("ppc", buildDir, installDir, cxxcompiler="g++-3.3", cccompiler="gcc-3.3", 
                        target="10.3", flags="-DMAC_OS_X_VERSION_MAX_ALLOWED=1040")
        else:
            doMacLipoBuild("ppc", buildDir, installDir)

        doMacLipoBuild("i386", buildDir, installDir)

        result = os.system("python %s/distrib/scripts/mac/lipo-dir.py %s %s %s" % (wxRootDir, installDir + "/ppc", installDir + "/i386", installDir))
        fname = os.path.abspath(installDir + '/bin/wx-config') 
        data = open(fname).read()
        data = data.replace('ppc/', '')
        data = data.replace('i386/', '')
        open(fname, 'w').write(data)
        
        shutil.rmtree(installDir + "/ppc")
        shutil.rmtree(installDir + "/i386")
  
if not isLipo:
    exitIfError(wxBuilder.build(dir=buildDir, options=args), "Error building")
    
    if options.wxpython and os.path.exists(contribDir):
        exitIfError(wxBuilder.build(os.path.join(contribDir, "gizmos"), options=args), "Error building gizmos")
        exitIfError(wxBuilder.build(os.path.join(contribDir, "stc"),options=args), "Error building stc")
        
    if options.install:
        wxBuilder.install()
        
        if options.wxpython and os.path.exists(contribDir):
            exitIfError(wxBuilder.install(os.path.join(contribDir, "gizmos")), "Error building gizmos")
            exitIfError(wxBuilder.install(os.path.join(contribDir, "stc")), "Error building stc")
        
if options.mac_framework:

    def renameLibrary(libname, frameworkname):
        reallib = libname
        links = []
        while os.path.islink(reallib):
            links.append(reallib)
            reallib = "lib/" + os.readlink(reallib)
            
        print "reallib is %s" % reallib
        os.system("mv -f %s lib/%s.dylib" % (reallib, frameworkname))
        
        for link in links:
            os.system("ln -s -f %s.dylib %s" % (frameworkname, link))

    os.chdir(installDir)
    build_string = ""
    if options.debug:
        build_string = "d"
    version = commands.getoutput("bin/wx-config --release")
    basename = commands.getoutput("bin/wx-config --basename")
    configname = commands.getoutput("bin/wx-config --selected-config")
    
    os.system("ln -s -f bin Resources")
    
    # we make wx the "actual" library file and link to it from libwhatever.dylib
    # so that things can link to wx and survive minor version changes
    renameLibrary("lib/lib%s-%s.dylib" % (basename, version), "wx")
    os.system("ln -s -f lib/wx.dylib wx")
    
    os.system("ln -s -f include/wx Headers")
    
    for lib in ["GL", "STC", "Gizmos", "Gizmos_xrc"]:  
        libfile = "lib/lib%s_%s-%s.dylib" % (basename, lib.lower(), version)
        if os.path.exists(libfile):
            frameworkDir = "framework/wx%s/%s" % (lib, version)
            if not os.path.exists(frameworkDir):
                os.makedirs(frameworkDir)
            renameLibrary(libfile, "wx" + lib)
            os.system("ln -s -f ../../../%s %s/wx%s" % (libfile, frameworkDir, lib))        
    
    for lib in glob.glob("lib/*.dylib"):
        if not os.path.islink(lib):
            corelibname = "lib/lib%s-%s.0.dylib" % (basename, version)
            os.system("install_name_tool -id %s %s" % (os.path.join(installDir, lib), lib))
            os.system("install_name_tool -change %s %s %s" % (os.path.join(installDir, "i386", corelibname), os.path.join(installDir, corelibname), lib))
            
    os.chdir("include")
    
    header_template = """
    
#ifndef __WX_FRAMEWORK_HEADER__
#define __WX_FRAMEWORK_HEADER__

%s

#endif // __WX_FRAMEWORK_HEADER__
"""
    headers = ""
    header_dir = "wx-%s/wx" % version
    for include in glob.glob(header_dir + "/*.h"):
        headers += "wx/" + os.path.basename(include) + "\n"
        
    framework_header = open("wx.h", "w")
    framework_header.write(header_template % headers)
    framework_header.close()
    
    os.system("ln -s -f %s wx" % header_dir)
    os.system("ln -s -f ../../../lib/wx/include/%s/wx/setup.h wx/setup.h" % configname)
    
    os.chdir(os.path.join(installDir, "..", ".."))
    os.system("ln -s -f %s Versions/Current" % os.path.basename(installDir))
    os.system("ln -s -f Versions/Current/Headers Headers")
    os.system("ln -s -f Versions/Current/Resources Resources")
    os.system("ln -s -f Versions/Current/wx wx")
    
