#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import SCons
import shutil
import subprocess
import time
import datetime
import SCons.Script as SCons

from build import util, depends

mixxx_version = util.get_mixxx_version()
branch_name = util.get_branch_name()
vcs_revision = util.get_revision()
vcs_name = util.get_current_vcs()
print "WE ARE IN:", os.getcwd()
print "Building ", branch_name, " - rev.", vcs_revision

plugins = []

# Grab these from the SConstruct above us
Import('build')
Import('sources')
Import('soundsource_plugins')
Import('mixxxminimal_plugins')

env = build.env
flags = build.flags

#Tell SCons to build Mixxx
#=========================
if build.platform_is_windows:
        dist_dir = 'dist%s' % build.bitwidth
        # Populate the stuff that changes in the .rc file
        fo = open(File('#src/mixxx.rc.include').abspath, "w")

        str_list = []
        str_list.append('#define VER_FILEVERSION             ')
        # Remove anything after ~ or - in the version number and replace the dots with commas
        str_list.append(mixxx_version.partition('~')[0].partition('-')[0].replace('.',','))
        if vcs_revision:
            str_list.append(','+str(vcs_revision))
        str_list.append('\n')

        str_list.append('#define VER_PRODUCTVERSION          ')
        str_list.append(mixxx_version.partition('~')[0].partition('-')[0].replace('.',','))
        if vcs_revision:
            str_list.append(','+str(vcs_revision))
        str_list.append('\n')

        import datetime
        now = datetime.datetime.now()
        str_list.append('#define CUR_YEAR                    "'+str(now.year)+'"\n\n')

        if build.build_is_debug:
            str_list.append('#define DEBUG                       1\n')
        if 'pre' in mixxx_version.lower():
            str_list.append('#define PRERELEASE                  1\n')

        fo.write(''.join(str_list))
        fo.close()

        mixxx_bin = env.Program('mixxx',
                            [sources, env.RES('#src/mixxx.rc')],
                            LINKCOM = [env['LINKCOM'], 'mt.exe -nologo -manifest ${TARGET}.manifest -outputresource:$TARGET;1'])
elif build.platform_is_osx:
        # Bug #1258435: executable name must match CFBundleExecutable in the
        # Info.plist. For codesigned bundles it seems the CFBundleExecutable
        # must match the bundle name or else we SIGILL at startup (not sure
        # why).
        mixxx_bin = env.Program('Mixxx', sources)
else:
        mixxx_bin = env.Program('mixxx', sources)

test_bin = None
def build_tests():
        global test_bin
        test_files = Glob('test/*.cpp', strings=True)
        test_env = env.Clone()
        test_env.Append(CPPPATH="#lib/gtest-1.7.0/include")
        test_env.Append(CPPPATH="#lib/gmock-1.7.0/include")
        test_files = [test_env.StaticObject(filename)
                      if filename !='main.cpp' else filename
                      for filename in test_files]
        mixxx_sources = [filename for filename in sources if filename != 'main.cpp']
        test_sources = (test_files + mixxx_sources)

        env.Append(LIBPATH="#lib/gtest-1.7.0/lib")
        env.Append(LIBS = 'gtest')

        env.Append(LIBPATH="#lib/gmock-1.7.0/lib")
        env.Append(LIBS = 'gmock')

        if build.platform_is_windows:
                # TODO(rryan): Build Mixxx core as a shared object and link it
                # into mixxx and mixxx-test. We could build both in different
                # environments right now but then automoc and protoc get run in
                # both environments which makes SCons unhappy.
                # Currently both executables are built with /subsystem:windows
                # and the console is attached manually
                test_bin = env.Program(
                        'mixxx-test', [test_sources, env.RES('#src/mixxx.rc')],
                        LINKCOM = [env['LINKCOM'], 'mt.exe -nologo -manifest ${TARGET}.manifest -outputresource:$TARGET;1'])
        else:
                test_bin = env.Program(target='mixxx-test', source=test_sources)

        env.Alias('mixxx-test', test_bin)

        if not build.platform_is_windows:
                Command("../", test_bin, Copy("$TARGET", "$SOURCE"))

def run_tests():
        ret = Execute("./mixxx-test")
        if ret != 0:
                print "WARNING: Not all tests pass. See mixxx-test output."
                Exit(ret)

if int(build.flags['test']):
        print "Building tests."
        build_tests()

if 'test' in BUILD_TARGETS:
        print "Running tests."
        run_tests()

def construct_version(build, mixxx_version, branch_name, vcs_revision):
        if branch_name.startswith('release-'):
                branch_name = branch_name.replace('release-', '')

        # Include build type in the filename.
        build_type = 'release' if build.build_is_release else 'debug'

        # New, simpler logic: mixxx version, branch name, git revision,
        # release/build. Example: mixxx-1.12.0-master-gitXXXX-release
        return "%s-%s-%s%s-%s" % (mixxx_version, branch_name, vcs_name,
                                  vcs_revision, build_type)

def ubuntu_construct_version(build, mixxx_version, branch_name, vcs_revision,
                             ubuntu_version):
        # Underscores are not ok in version names, dashes are fine though.
        if branch_name:
                branch_name = branch_name.replace('_', '-')
        # In release mode, we only use the version and ubuntu_version
        if build.build_is_release:
                return "%s-%s" % (mixxx_version, ubuntu_version)
        elif branch_name.startswith('release-'):
                return "%s-%s%s" % (mixxx_version, vcs_name, vcs_revision)
        # In a debug build, we must include the Mixxx version since it is the
        # base "upstream" version in the Debian version number. Also include the
        # branch name and VCS version so we know where the build came from.
        # See: https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version
        # e.g. 1.12.0-alpha~master-gitXXXX~ppa1~trusty1
        return "%s~%s-%s%s-%s" % (mixxx_version, branch_name, vcs_name,
                                  vcs_revision, ubuntu_version)

#Set up the install target
#=========================

# flags['prefix'] = ARGUMENTS.get('prefix', '/usr/local')
# if not os.path.exists(flags['prefix']):
#         print "Error: Prefix path does not exist!"
#         Exit(1)
# else:
#        unix_share_path = flags['prefix'] + "/share"
#        unix_bin_path   = flags['prefix'] + "/bin"

bundle_pdbs = build.platform_is_windows and (
        build.build_is_debug or
        SCons.ARGUMENTS.get('bundle_pdbs', '') in ('yes', 'y', '1'))

#Mixxx binary
binary_files = [mixxx_bin];
if test_bin is not None:
        binary_files.append(test_bin)
        if bundle_pdbs:
                binary_files.append(
                        Glob('#' + os.path.join(build.build_dir, 'mixxx-test.pdb')))

if bundle_pdbs:
        binary_files.append(
                Glob('#' + os.path.join(build.build_dir, 'mixxx.pdb')))

#Soundsource plugins
soundsource_plugin_files = soundsource_plugins

#VAMP beat tracking and key detection plugin
libmixxxminimal_vamp_plugin = mixxxminimal_plugins

#Skins
skin_files = Glob('#res/skins/*')

#Controller mappings
controllermappings_files = Glob('#res/controllers/*')

# Translation files
translation_files = Glob('#res/translations/*.qm')

# Font files
font_files = Glob('#res/fonts/*')

#Keyboard mapping(s)
keyboardmappings_files = Glob('#res/keyboard/*')

#Promo tracks
promotracks_files = Glob('#res/promo/*')

#Documentation
docs_files = Glob('#./LICENSE')
docs_files += Glob('#./README')
docs_files += Glob('#./Mixxx-Manual.pdf')

#.desktop file for KDE/GNOME menu
dotdesktop_files = Glob('#res/linux/mixxx.desktop')

#.appdata.xml file for KDE/GNOME AppStream iniative
dotappstream_files = Glob('#res/linux/mixxx.appdata.xml')

#udev rule file for USB HID and Bulk controllers
hidudev_files = Glob('#res/linux/mixxx.usb.rules')

#Icon file for menu entry
icon_files = Glob('#res/images/mixxx-icon.png')

#Images for preferences dialog
image_files = Glob('#res/images/preferences/*')  # These are compiled in to the "mixxx" binary through mixxx.qrc

#Windows DLLs

# TODO: Use reference to SharedLibrary for libsndfile and others, glob only gets
# all files on 2+ builds after a clean.
dll_files = []
if build.toolchain_is_msvs:
        mixxx_winlib_path = SCons.ARGUMENTS.get('winlib', '..\\..\\..\\mixxx-win32lib-msvc90-release')
        # skip the MSVC DLLs incase they're in there too
        dll_files.extend(Glob('%s/*.dll' % mixxx_winlib_path))
        dll_files.extend(Glob('%s/lib/*.dll' % mixxx_winlib_path))

        if bundle_pdbs:
                dll_files.extend(Glob('%s/*.pdb' % mixxx_winlib_path))
                dll_files.extend(Glob('%s/lib/*.pdb' % mixxx_winlib_path))
elif build.crosscompile and build.platform_is_windows and build.toolchain_is_gnu:
        # We're cross-compiling, grab these from the crosscompile bin
        # folder. How should we be doing this?
        dll_files = Glob('#/../../mixxx-win%slib-crossmingw' % build.bitwidth)

qt_modules = depends.Qt.enabled_modules(build)
qt5 = depends.Qt.qt5_enabled(build)

if qt5:
        suffix = 'd.dll' if build.build_is_debug else '.dll'
        qt_modules = ['$QTDIR/lib/' + module.replace('Qt', 'Qt5') + suffix
                      for module in qt_modules]
        dll_files.extend(qt_modules)
else:
        suffix = 'd4.dll' if build.build_is_debug else '4.dll'
        qt_modules = ['$QTDIR/lib/' + module + suffix for module in qt_modules]
        dll_files.extend(qt_modules)

# Qt imageformats plugin
imgfmtdll_files = []
qt_imagesformats = depends.Qt.enabled_imageformats(build)

if qt5:
        suffix = 'd.dll' if build.build_is_debug else '.dll'
        qt_imagesformats = ['$QTDIR/plugins/imageformats/' + module + suffix for module in qt_imagesformats]
        imgfmtdll_files.extend(qt_imagesformats)
else:
        suffix = 'd4.dll' if build.build_is_debug else '4.dll'
        qt_imagesformats = ['$QTDIR/plugins/imageformats/' + module + suffix for module in qt_imagesformats]
        imgfmtdll_files.extend(qt_imagesformats)

sqldll_files = []
if int(flags.get('qt_sqlite_plugin', 0)):
        if qt5:
                # TODO(rryan): Add the SQLite DLL For Qt5.
                pass
        else:
                suffix = 'd4.dll' if build.build_is_debug else '4.dll'
                # Qt SQLite plugin
                sqldll_files = ['$QTDIR/plugins/sqldrivers/qsqlite' + suffix]

if build.platform_is_linux or build.platform_is_bsd:
        flags['prefix'] = ARGUMENTS.get('prefix', '/usr/local')
        if not os.path.exists(flags['prefix']):
                print "Error: Prefix path does not exist!"
                Exit(1)
        else:
                #install_root is used in Debian/Ubuntu packaging (check the debian/rules file in the Ubuntu package)
                #Basically, the flags['prefix'] is compiled into strings in Mixxx, whereas the install_root is not. When you're
                #building a Debian package, pbuilder wants to install Mixxx to a temporary directory, but you still need
                #the compiled-in strings using /usr as the prefix. That's why we have install_root and flags['prefix'].
                install_root = ARGUMENTS.get('install_root', flags['prefix'])
                print "Install root: " + install_root
                unix_share_path = os.path.join(install_root,
                    env.get('SHAREDIR', default='share'))
                unix_bin_path = os.path.join(install_root,
                    env.get('BINDIR', default='bin'))
                unix_lib_path = os.path.join(install_root,
                    env.get('LIBDIR', default='lib'))

                binary = env.Install(unix_bin_path, binary_files)
                skins = env.Install(os.path.join(unix_share_path, 'mixxx', 'skins'), skin_files)
                fonts = env.Install(os.path.join(unix_share_path, 'mixxx', 'fonts'), font_files)
                vamp_plugin =  env.Install(
                        os.path.join(unix_lib_path, 'mixxx', 'plugins', 'vamp'),
                        libmixxxminimal_vamp_plugin)

                soundsource_plugins = env.Install(
                        os.path.join(unix_lib_path, 'mixxx', 'plugins', 'soundsource'),
                        soundsource_plugin_files)
                controllermappings = env.Install(os.path.join(unix_share_path, 'mixxx', 'controllers'), controllermappings_files)
                translations = env.Install(os.path.join(unix_share_path, 'mixxx', 'translations'), translation_files)
                keyboardmappings = env.Install(os.path.join(unix_share_path, 'mixxx', 'keyboard'), keyboardmappings_files)
                dotdesktop = env.Install(os.path.join(unix_share_path, 'applications'), dotdesktop_files)
                dotappstream = env.Install(os.path.join(unix_share_path, 'appdata'), dotappstream_files)
                docs = env.Install(os.path.join(unix_share_path, 'doc', 'mixxx'), docs_files)
                icon = env.Install(os.path.join(unix_share_path, 'pixmaps'), icon_files)
                promotracks = env.Install(os.path.join(unix_share_path, 'mixxx', 'promo'), promotracks_files)

                # NOTE(rryan): Hack to detect when we're Debian packaging.
                building_debian_package = 'debian/tmp/usr' in install_root
                udev_root = '/etc/udev/rules.d'
                hidudev = env.Install(udev_root, hidudev_files)

                #Makes each of those Install builders get fired off when you run "scons install" :)
                env.Alias('install', binary)
                env.Alias('install', skins)
                env.Alias('install', fonts)
                env.Alias('install', soundsource_plugins)
                env.Alias('install', controllermappings)
                env.Alias('install', translations)
                env.Alias('install', keyboardmappings)
                env.Alias('install', docs)
                env.Alias('install', dotdesktop)
                env.Alias('install', dotappstream)
                env.Alias('install', icon)
                env.Alias('install', promotracks)
                env.Alias('install', vamp_plugin)

                if not building_debian_package and os.access(udev_root, os.W_OK):
                        env.Alias('install', hidudev)

                #Delete the old Mixxx installation (because SCONS won't overwrite it)
                #if 'install' in COMMAND_LINE_TARGETS:
                        #os.system('scons -c install')
                        #Delete(unix_share_path + "/mixxx/skins")
                        #print "Copying skins..."
                        #env.Command(unix_share_path + "/mixxx/skins", skin_files, Copy("$TARGET", "$SOURCE"), source_scanner = DirScanner)
                        #Copy(unix_share_path + "/.ixxx/skins", skin_files)
                        #Delete(unix_bin_path + "mixxx")

                        #Delete(unix_share_path + "/mixxx/controllers")
                        #Delete(unix_share_path + "/mixxx/keyboard")

#Build the Mixxx.app bundle
if build.platform_is_osx and 'bundle' in COMMAND_LINE_TARGETS:
        #Mixxx build variables
        VOLNAME="Mixxx" #tmp tmp tmp, it's unclean to pass this into build_dmg this way. perhaps pass it in the env?
        ARCH = 'ppc' if build.machine in ['powerpc', 'powerpc64'] else 'macintel'
        ARCH += ("64" if build.machine_is_64bit else "32")

        DMG_ICON="#res/osx/VolumeIcon.icns"

        # this is a BIG HACK to support Qt's plugins (since Qt *requires* that
        # it's plugins be in specific subdirectories, which OS X doesn't really
        # play nice with)

        # NOTE(rryan): Only include the SQLite plugin if we are building Qt in
        # sqlite_plugin mode.
        sql_dylibs = []
        if int(flags.get('qt_sqlite_plugin', 0)):
                sql_dylibs = ["libqsqlite.dylib"]

        qt_plugins = (
                [("iconengines", e) for e in ["libqsvgicon.dylib"]] +
                [("imageformats", e) for e in ["libqgif.dylib", "libqjpeg.dylib", "libqsvg.dylib"]] +
                [("sqldrivers", e) for e in sql_dylibs] +
                [("accessible", e) for e in ["libqtaccessiblewidgets.dylib"]])
        #Left out libqmng and libqtiff to save space.

        # Concatenate the SoundSource plugins to our list of plugins (converting
        # from SCons File nodes to strings)
        for x in soundsource_plugins:
                plugins.append(x.get_abspath())

        for x in mixxxminimal_plugins:
                plugins.append(x.get_abspath())

        resource_map = {}
        translation_files = Glob('#res/translations/*.qm')
        for tfile in translation_files:
                resource_map[str(tfile)] = 'translations'

        qtdir = build.env['QTDIR']
        qt_frameworks = depends.Qt.find_framework_libdir(qtdir, qt5)
        if not qt_frameworks:
                raise Exception('Could not find frameworks in Qt directory: %s' % qtdir)
        #qt_menu.nib for Cocoa Qt 4.7+
        menu_nib = os.path.join(qt_frameworks, 'QtGui.framework',
                                'Resources', 'qt_menu.nib')
        otool_local_paths = [os.path.expanduser("~/Library/Frameworks"),
                             qt_frameworks,
                             "/Library/Frameworks",
                             "/Network/Library/Frameworks",
                             "/usr/local/lib",
                             "/opt/local/lib",
                             "/sw/local/lib"]
        otool_system_paths = ["/System/Library/Frameworks",
                              "/Network/Library/Frameworks",
                              "/usr/lib"]
        mixxx_osxlib_path = SCons.ARGUMENTS.get('osxlib', None)
        if mixxx_osxlib_path:
                otool_local_paths = [mixxx_osxlib_path,] + otool_local_paths

        qtplugindir = SCons.ARGUMENTS.get('qtplugindir', None)
        if not qtplugindir:
                #qtplugindir = '/Developer/Applications/Qt/'
                qtplugindir = qtdir
        sources = [mixxx_bin,
                   '#res/osx/application.icns',
                   Dir('#res/skins/'),
                   Dir('#res/controllers/'),
                   Dir('#res/fonts/'),
                   translation_files,
                   Dir('#res/keyboard/'),
                   Dir('#res/doc/'),
                   Dir('#res/promo/'),
                   Dir(menu_nib),
                   File("#README"),
                   File("#LICENSE")]
        bundle = env.App(
                "Mixxx_bundle",
                sources,
                PLUGINS=plugins, ##XXX test what happens if we don't pass any plugins
                #Qt plugins ((Qt *NEEDS* its plugins in specific locations or it refuses to find them, however this is clearly awkward to write out like this.. maybe))
                QT_HACK = [(p_tgt_dir, os.path.join(qtplugindir, "plugins", p_tgt_dir, p)) for p_tgt_dir, p in qt_plugins], #sigh :(
                STRIP=build.build_is_release,
                APP_RESOURCES_MAP=resource_map,
                IDENTIFIER="org.mixxx.mixxx",
                DISPLAY_NAME="Mixxx",
                VERSION=mixxx_version,
                SHORT_VERSION=mixxx_version,
                COPYRIGHT="Copyright © 2001-%s Mixxx Development Team" % datetime.datetime.now().year,
                # WARNING(rjryan): If you change this, you must also change it
                # in the build/osx/product_definition.plist.
                MINIMUM_OSX_VERSION="10.6.0",
                CATEGORY="public.app-category.music",
                OTOOL_LOCAL_PATHS=otool_local_paths,
                OTOOL_SYSTEM_PATHS=otool_system_paths,
                FOR_APP_STORE=True,
                )
        #env.Default(mixxx_bin) #todo: make the Default() just the program itself *globally* (not just for OS X); bundle should be a separate target
        env.Alias('bundle', bundle)

        codesign_installer_identity = SCons.ARGUMENTS.get('osx_codesign_installer_identity', None)
        codesign_application_identity = SCons.ARGUMENTS.get('osx_codesign_application_identity', None)
        codesign_keychain = SCons.ARGUMENTS.get('osx_codesign_keychain', None)
        codesign_keychain_password = SCons.ARGUMENTS.get('osx_codesign_keychain_password', None)
        codesign_entitlements = SCons.ARGUMENTS.get('osx_codesign_entitlements', None)
        # CodeSign needs to take sources for it source so that there is an input
        # that changse. Otherwise SCons will think the CodeSign target is up to
        # date and not run it.
        codesign = env.CodeSign(
                'Mixxx_codesign',
                sources,
                CODESIGN_INSTALLER_IDENTITY=codesign_installer_identity,
                CODESIGN_APPLICATION_IDENTITY=codesign_application_identity,
                CODESIGN_KEYCHAIN=codesign_keychain,
                CODESIGN_KEYCHAIN_PASSWORD=codesign_keychain_password,
                CODESIGN_ENTITLEMENTS=codesign_entitlements)
        env.AlwaysBuild(codesign)
        env.Alias('sign', codesign)

        package_name = 'mixxx'
        package_version = construct_version(build, mixxx_version, branch_name,
                                            vcs_revision)
        dmg_name = '%s-%s-%s' % (package_name, package_version, ARCH)
        dmg = env.Dmg(dmg_name, [bundle, ] + docs_files, VOLNAME=VOLNAME, ICON = DMG_ICON)
        env.Alias('package', dmg)

if build.platform_is_windows:
        base_dist_dir = '#' + dist_dir
        skins = env.Install(os.path.join(base_dist_dir, "skins"), skin_files)
        controllermappings = env.Install(os.path.join(base_dist_dir, "controllers"), controllermappings_files)
        fonts = env.Install(os.path.join(base_dist_dir, "fonts"), font_files)
        translations = env.Install(os.path.join(base_dist_dir, "translations"), translation_files)
        keyboardmappings = env.Install(os.path.join(base_dist_dir, "keyboard"), keyboardmappings_files)
        docs = env.Install(os.path.join(base_dist_dir, "doc/"), docs_files)
        promotracks = env.Install(os.path.join(base_dist_dir, "promo/"), promotracks_files)
        #icon = env.Install(base_dist_dir+"", icon_files)
        dlls = env.Install(base_dist_dir+"/", dll_files)
        soundsource_plugins = env.Install(os.path.join(base_dist_dir, "plugins", "soundsource/"),
                                          soundsource_plugin_files)
        vamp_plugins = env.Install(os.path.join(base_dist_dir, "plugins", "vamp/"),
                                   libmixxxminimal_vamp_plugin)
        binary = env.Install(base_dist_dir+"/", binary_files)

        #Always trigger these install builders when compiling on Windows
        env.Alias('mixxx', skins)
        env.Alias('mixxx', controllermappings)
        env.Alias('mixxx', fonts)
        env.Alias('mixxx', translations)
        env.Alias('mixxx', keyboardmappings)
        env.Alias('mixxx', promotracks)
        env.Alias('mixxx', docs)
        env.Alias('mixxx', dlls)
        env.Alias('mixxx', soundsource_plugins)
        env.Alias('mixxx', vamp_plugins)
        #env.Alias('mixxx', icon)
        env.Alias('mixxx', binary)

        # imageformats DLL
        if imgfmtdll_files:
                imageformats_dll = env.Install(os.path.join(base_dist_dir, "imageformats"), imgfmtdll_files)
                env.Alias('mixxx', imageformats_dll)

        # QSQLite DLL
        if sqldll_files:
                sql_dlls = env.Install(os.path.join(base_dist_dir, "sqldrivers"), sqldll_files)
                env.Alias('mixxx', sql_dlls)

def win32_find_program_via_registry(program_name):
    # Windows registry access to find where program is installed
    import _winreg
    hklm = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE)
    program_location_handle = None
    try:
        program_location_handle = _winreg.OpenKey(hklm, "SOFTWARE\\"+program_name, 0, _winreg.KEY_READ)
    except WindowsError:
        program_location_handle = None

    if not program_location_handle:
        try:
            program_location_handle = _winreg.OpenKey(hklm, "SOFTWARE\\Wow6432Node\\"+program_name, 0, _winreg.KEY_READ)
        except WindowsError:
            program_location_handle = None

    program_location = _winreg.QueryValue(program_location_handle, None)
    if not program_location:
        try:
            program_location_tuple = _winreg.QueryValueEx(program_location_handle, "Path")
            program_location = program_location_tuple[0]
        except WindowsError:
            program_location_handle = None

    _winreg.CloseKey(hklm)
    return program_location

def BuildRelease(target, source, env):
    print
    print "==== Mixxx Post-Build Checks ===="
    print
    print "You have built version %s" % mixxx_version
    print
    if build.build_is_debug:
        print "YOU ARE ABOUT TO PACKAGE A DEBUG BUILD!!"
        print
    print "Binary has size ",
    if build.platform_is_windows:
        os.system('for %I in ('+dist_dir+'\mixxx.exe) do @echo %~zI')
    else:
        os.system('ls -lh '+dist_dir+'/mixxx.exe | cut -d \' \' -f 5')
    print
    print "Installer file ",
    package_name = 'mixxx'

    package_version = construct_version(build, mixxx_version, branch_name,
                                        vcs_revision)
    arch = "x64" if build.machine_is_64bit else "x86"
    exe_name = '%s-%s-%s.exe' % (package_name, package_version, arch)
    print exe_name
    print
    print "Top line of README, check version:"
    if build.platform_is_windows:
        os.system('for /l %l in (1,1,1) do @for /f "tokens=1,2* delims=:" %a in (\'findstr /n /r "^" README ^| findstr /r "^%l:"\') do @echo %b')
    else:
        os.system('head -n 1 README')
    print
    print "Top 2 lines of LICENSE, check version and copyright dates:"
    if build.platform_is_windows:
        os.system('for /l %l in (1,1,2) do @for /f "tokens=1,2* delims=:" %a in (\'findstr /n /r "^" LICENSE ^| findstr /r "^%l:"\') do @echo %b')
    else:
        os.system('head -n 2 LICENSE')
    print
    #if (raw_input("Go ahead and build installer (yes/[no])? ") == "yes"):
    if True:
        # TODO(XXX): Installing a runtime isn't specific to MSVS?
        if build.toolchain_is_msvs:
            redist_file = 'vcredist_%s.exe' % arch
            print "Searching for the Visual C++ DLL installer package", redist_file
            # Check for the runtime installer in the winlib root.
            redist_path = '%s' % os.path.join(mixxx_winlib_path, redist_file)
            print "   ", redist_path,
            if not os.path.isfile(redist_path):
                raise Exception('Could not find the MSVC++ runtime installer.')

        print "Now building installation package..."
        command_flag = None
        nsis_path = None
        if not build.crosscompile and build.platform_is_windows:
            nsis_directory = win32_find_program_via_registry("NSIS")
            nsis_path = '"%s"' % os.path.join(nsis_directory, "makensis.exe")
            command_flag = '/'
        elif build.crosscompile and build.platform_is_windows:
            nsis_path = 'makensis'
            command_flag = '-'

        if not nsis_path:
            print "Cannot find NSIS. Do you have it installed?"
        else:
            # Call the NSIS build
            buildwin64 = "/Dx64=1" if build.machine_is_64bit else ''
            command = '%(path)s /DPACKAGE_NAME=%(package_name)s /DPRODUCT_VERSION=%(version)s /DQTDIR=%(qtpath)s /DWINLIB_PATH=%(winlibpath)s %(64bit)s build\\nsis\\Mixxx.nsi' % \
                {'path': nsis_path,
                 'package_name': exe_name,
                 'version': mixxx_version,
                 'qtpath': build.env['QTDIR'],
                 'winlibpath': mixxx_winlib_path,
                 '64bit': buildwin64}
            print "Using command: " + command
            subprocess.check_call(command)
    else:
        print "Aborted building installer"

# Do release things
versionbld = Builder(action = BuildRelease, suffix = '.foo', src_suffix = '.bar')
env.Append(BUILDERS = {'BuildRelease' : versionbld})

if 'makerelease' in COMMAND_LINE_TARGETS:
        makerelease = env.BuildRelease('', binary_files)
        env.Alias('makerelease', makerelease)

def ubuntu_append_changelog(debian_dir,
                            package_name, package_version,
                            description,
                            distro='lucid',
                            urgency='low',
                            author="Mixxx Buildbot <builds@mixxx.org>"):
        now_formatted = time.strftime("%a,  %d %b %Y %H:%M:%S +0000", time.gmtime())
        new_entry = [
                "%s (%s) %s; urgency=%s" % (package_name, package_version, distro, urgency),
                "",
                description,
                "",
                " -- %s  %s" % (author, now_formatted),
                "",
                ]
        lines = []
        with open(os.path.join(debian_dir, 'changelog'), 'r') as changelog:
                lines = list(changelog)
        with open(os.path.join(debian_dir, 'changelog'), 'w') as changelog:
                changelog.writelines(["%s\n" % x for x in new_entry])
                changelog.writelines(lines)

def ubuntu_cleanup():
        os.system('rm -rf ubuntu')
        os.mkdir('ubuntu')

# Build the Ubuntu package
def BuildUbuntuPackage(target, source, env):
        global mixxx_version
        print
        print "==== Mixxx Post-Build Checks ===="
        print
        print "You have built version ", mixxx_version
        print
        print
        print "Top line of README, check version:"
        os.system('head -n 1 README')
        print
        print "Top 2 lines of LICENSE, check version and copyright dates:"
        os.system('head -n 2 LICENSE')
        print
        print "Top line of debian/ubuntu changelog, check version:"
        os.system('head -n 1 build/debian/changelog')
        print

        print "Now building DEB package..."
        print

        arch = 'amd64' if build.machine_is_64bit else 'i386'

        package_target = ARGUMENTS.get('package', None)
        ubuntu_distros = ARGUMENTS.get('ubuntu_dist', None)
        if ubuntu_distros is None:
                print "You did not specify an Ubuntu distribution to target. Specify one with the ubuntu_dist flag."
                # TODO(XXX) default to their current distro? the .pbuilderrc does this
                return
        ubuntu_version = ARGUMENTS.get('ubuntu_version', '0ubuntu1')
        ubuntu_ppa = ARGUMENTS.get('ubuntu_ppa', None)

        ubuntu_distros = ubuntu_distros.split(',')

        # Big hack for beta PPA upload. We need LP to believe that our original
        # package version is always changing otherwise it will reject our orig
        # source tarball.
        if ubuntu_ppa and 'mixxxbetas' in ubuntu_ppa:
                mixxx_version = '%s-%s%s' % (mixxx_version, vcs_name, vcs_revision)

        # Destroy ubuntu/ and create it
        ubuntu_cleanup()

        package_name = 'mixxx'

        # directory and original tarball need to have the upstream-release
        # version, NOT the package version. For example:
        # upstream version: 1.10.0-beta1
        # package version: 1.10.0-beta1-0ubuntu1~bzr2206
        # directory name: mixxx-1.10.0-beta1
        # original tarball: mixxx_1.10.0-beta1.orig.tar.gz

        mixxx_dir = '%s-%s' % (package_name, mixxx_version)
        # The underscore is super important here to make the deb package work
        mixxx_tarball = "%s_%s.orig.tar.gz" % (package_name, mixxx_version)

        build_dir = os.path.join('ubuntu', mixxx_dir)

        if os.path.exists(build_dir):
                print "* Cleaning up %s (cwd: %s)" % (build_dir, os.getcwd())
                print
                os.system('rm -rf %s' % build_dir) # be careful.

        # TODO: make a get flags arg to accept a revision which can override this and checkout of a specific SVN rev for the package

        # Export the source folder
        print "* Exporting source folder from current workspace (%s rev: %s)" % (vcs_name,
                                                                                 vcs_revision)
        print
        util.export_source('.', build_dir)

        # Copy a patch to be included in the exported build sources (this can also be something like src/SConscript, /build/debian/rules)
        if os.path.exists('post-export-patch'):
                print "* Applying post export patch"
                print
                os.system('cp --dereference -r post-export-patch/* %s' % build_dir)

        # Write a build.h to the exported directory. Later code looks for a
        # build.h in the mixxx/ directory and moves it to build.build_dir/
        # instead of generating.
        util.write_build_header(os.path.join(build_dir, 'build.h'))

        os.chdir('ubuntu')

        # Tar the source code
        print "* Tarring source directory to '%s' ... (this can take a couple minutes)" % os.path.join(os.getcwd(), mixxx_tarball)
        print
        os.system('rm -f "%s"' % mixxx_tarball) #Remove old tarball
        os.system('tar --exclude build/debian --exclude=debian --exclude=debian/* -czf "%s" %s' % (mixxx_tarball, mixxx_dir))

        os.chdir(mixxx_dir)
        # Copy the debian folder from /build/debian to exported source folder root
        print "* Copying Debian build directory from build/debian to debian (cwd: %s)" % os.getcwd()
        print
        os.system('cp -r build/debian .')
        os.system('cp res/linux/mixxx.usb.rules ./debian/mixxx.mixxx-usb.udev')

        # Replace environment variables in the rules file.
        # TODO(rryan) something more elegant? I don't know a better way. When
        # Ubuntu build servers build us we don't get the chance to pass
        # environment variables in.
        with open('debian/rules', 'r') as fr:
                rules = fr.read()
                rules = rules.replace('$(MIXXX_BUILD)', build.build)

                with open('debian/rules', 'w') as fw:
                        fw.write(rules)

        for ubuntu_distro in ubuntu_distros:
                # if a control.$distro file exists, use it
                if os.path.exists('debian/control.%s' % ubuntu_distro):
                        os.system('cp debian/control.%s debian/control' % ubuntu_distro)
                # TODO(rryan) currently can only have version 1 within a distro
                ubuntu_distro_version = '%s~%s1' % (ubuntu_version, ubuntu_distro)
                package_version = ubuntu_construct_version(build, mixxx_version,
                                                           branch_name, vcs_revision,
                                                           ubuntu_distro_version)

                # Add a changelog record for this package
                if build.build_is_debug:
                        description = "  * Experimental build of branch '%s' at revision %s" % (branch_name, vcs_revision)
                        ubuntu_append_changelog('debian', package_name, package_version,
                                                description, distro=ubuntu_distro)
                else:
                        description = "  * New upstream release."
                        ubuntu_append_changelog('debian', package_name, package_version,
                                                description,
                                                distro=ubuntu_distro,
                                                author="RJ Ryan <rryan@mixxx.org>")

                # Run pbuilder
                print "* Starting pbuilder ...  (cwd: %s)" % os.getcwd()
                print

                command = ["MIXXX_BUILD=%s" % build.build,
                           "ARCH=%s" % arch,
                           "DIST=%s" % ubuntu_distro]

                if package_target == 'source':
                        # TODO(rryan) we have to figure out the key-signing situation
                        # here.
                        # Preserve the MIXXX_BUILD environment variable.
                        command.extend(['debuild', '-eMIXXX_BUILD', '-S', '-sa',])
                else:
                        command.extend(['pdebuild'])
                result = os.system(' '.join(command))

                if package_target == 'source':
                        if result == 0:
                                # TODO(rryan) check it actually succeeded
                                print "* Done! Signed source package is in ubuntu/"
                                print
                        else:
                                print "* Build failed."
                                print
                else:
                        result_path = "/var/cache/pbuilder/%s-%s/result/" % (ubuntu_distro, arch)
                        result_filename = "%s_%s_%s.deb" % (package_name, package_version, arch)
                        result_file = os.path.join(result_path, result_filename)

                        # Since we might build for multiple distros we need to
                        # insert the distro name into the filename.
                        # HACK(rryan): filenames for Ubuntu packaging in general
                        # are a big mess but we only distribute files in this
                        # code path (package_target != 'source') via
                        # downloads.mixxx.org so we may as well make the
                        # filenames match the Windows/OSX builds.
                        version = construct_version(build, mixxx_version,
                                                    branch_name, vcs_revision)
                        dest_filename = '-'.join((package_name, version,
                                                  ubuntu_distro, arch))
                        dest_deb_filename = "%s.deb" % dest_filename
                        # Also rename the source tarball.
                        dest_tar_filename = "%s.tar.gz" % dest_filename

                        # ubuntu/ is one folder up
                        dest_deb_file = os.path.join('..', dest_deb_filename)
                        dest_tar_file = os.path.join('..', dest_tar_filename)

                        source_tar_file = os.path.join('..', mixxx_tarball)
                        if os.path.exists(source_tar_file):
                                shutil.move(source_tar_file, dest_tar_file)

                        if result == 0 and os.path.exists(result_file):
                                print "Done! Package and tarballs are in %s" % result_path
                                print "* Found package at '%s'. Copying to ubuntu/" % result_file
                                print
                                shutil.copyfile(result_file, dest_deb_file)
                        else:
                                print "* Build failed."
                                print

                # print "Signing the .deb changes file..."
                # os.system('sudo debsign /var/cache/pbuilder/result/*.changes')

                if ubuntu_ppa is not None:
                        # dput this changes file to the PPA
                        dput_command = 'dput %s ../%s_%s_source.changes' % (ubuntu_ppa, package_name, package_version)
                        print "* Uploading package for", ubuntu_distro, "to launchpad:", dput_command
                        os.system(dput_command)

        # Return back to the starting directory, otherwise you'll get a .sconsign.dblite error!
        os.chdir('../..')
        print "* Returning to starting working directory ...  (cwd: " + os.getcwd() + ")"
        print

#Build the Ubuntu package if "makeubuntu" was passed as an argument
versiondebbld = Builder(action = BuildUbuntuPackage) #, suffix = '.foo', src_suffix = '.bar')
env.Append(BUILDERS = {'BuildUbuntuPackage' : versiondebbld})

if 'makeubuntu' in COMMAND_LINE_TARGETS:
        makeubuntu = env.BuildUbuntuPackage("blah", "defs_version.h" ) #(binary_files)
        env.Alias('makeubuntu', makeubuntu)
