diff options
author | RJ Skerry-Ryan <rryan@mixxx.org> | 2018-10-26 08:53:36 -0700 |
---|---|---|
committer | RJ Skerry-Ryan <rryan@mixxx.org> | 2018-10-26 08:53:36 -0700 |
commit | d0e2d2d12d2b9853fa7aaba60928385df103fe23 (patch) | |
tree | 726e9fa36d10438f7fac4f0b22cbf3b9c1ed740d /SConscript | |
parent | cfa1f632ee1ff827effc036eee10e0a7fb65907f (diff) | |
parent | 398b533a847db24ddb6f3d30e1d2964f62308cbf (diff) |
Merge remote-tracking branch 'mixxxdj/master' into remove-vamp
Diffstat (limited to 'SConscript')
-rw-r--r-- | SConscript | 1158 |
1 files changed, 1158 insertions, 0 deletions
diff --git a/SConscript b/SConscript new file mode 100644 index 0000000000..1754889cc5 --- /dev/null +++ b/SConscript @@ -0,0 +1,1158 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from __future__ import print_function +import os +import SCons +import shutil +import subprocess +import time +import datetime +import glob +import uuid +from xml.dom import minidom +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') + +env = build.env +flags = build.flags + +# Make a static library of all Mixxx's sources. This library will be linked into +# both mixxx and mixxx-test. +mixxx_lib = env.StaticLibrary('libmixxx', + [source for source in sources + if str(source) != 'src/main.cpp']) +# mixxx.qrc must not be bundled into libmixxx.a since the linker will not link +# it into the resulting binary unless it is on the link command-line explicitly +# (it has no link-time symbols that are needed by anything in Mixxx). +mixxx_qrc = env.StaticObject(env.Qrc5('res/mixxx.cc', 'res/mixxx.qrc')) +# libmixxx.a needs to precede all other libraries so that symbols it requires +# end up in the linker's list of unresolved symbols before other libraries are +# searched for symbols. +env.Prepend(LIBS=mixxx_lib) +mixxx_main = env.StaticObject('src/main.cpp') + +#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_rc = env.RES('src/mixxx.rc') + mixxx_bin = env.Program('mixxx', + [mixxx_main, mixxx_qrc, 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', [mixxx_main, mixxx_qrc]) +else: + mixxx_bin = env.Program('mixxx', [mixxx_main, mixxx_qrc]) + +# For convenience, copy the Mixxx binary out of the build directory to the +# root. Don't do it on windows because the binary can't run on its own and needs +# the DLLs present with it. +if not build.platform_is_windows: + copy_mixxx_bin = Command("../mixxx", mixxx_bin, Copy("$TARGET", "$SOURCE")) + Default(copy_mixxx_bin) +else: + Default(mixxx_bin) + +test_bin = None +def define_test_targets(default=False): + global test_bin + test_files = Glob('src/test/*.cpp', strings=True) + test_env = env.Clone() + + test_env.Append(CPPPATH="lib/gtest-1.7.0/include") + test_env.Append(LIBPATH="lib/gtest-1.7.0") + test_env.Append(LIBS=['gtest']) + + test_env.Append(CPPPATH="lib/gmock-1.7.0/include") + test_env.Append(LIBPATH="lib/gmock-1.7.0") + test_env.Append(LIBS=['gmock']) + + test_env.Append(CPPPATH="lib/benchmark/include") + test_env.Append(LIBPATH="lib/benchmark") + test_env.Append(LIBS=['benchmark']) + + test_files = [test_env.StaticObject(filename) + if filename !='src/test/main.cpp' else filename + for filename in test_files] + + if build.platform_is_windows: + # For SHGetValueA in Google's benchmark library. + test_env.Append(LIBS=['Shlwapi']) + + # We want a terminal for tests. + if build.toolchain_is_msvs: + test_env['LINKFLAGS'].remove('/subsystem:windows,6.01') + test_env['LINKFLAGS'].append('/subsystem:console,6.01') + elif build.toolchain_is_gnu: + test_env['LINKFLAGS'].remove('--subsystem,windows') + test_env['LINKFLAGS'].append('--subsystem,console') + + # Currently both executables are built with /subsystem:windows + # and the console is attached manually + test_bin = test_env.Program( + 'mixxx-test', [test_files, mixxx_qrc, mixxx_rc], + LINKCOM = [env['LINKCOM'], 'mt.exe -nologo -manifest ${TARGET}.manifest -outputresource:$TARGET;1']) + else: + test_bin = test_env.Program('mixxx-test', [test_files, mixxx_qrc]) + + if not build.platform_is_windows: + copy_test_bin = Command("../mixxx-test", test_bin, Copy("$TARGET", "$SOURCE")) + env.Alias('mixxx-test', copy_test_bin) + # Running mixxx-test via a Command is hacky because it expects a + # target. Using the source '../mixxx-test' makes the Command + # depend on the Copy. + run_test = Command('mixxx-test-results', '../mixxx-test', './mixxx-test') + env.Alias('test', run_test) + + if default: + Default(copy_test_bin) + else: + env.Alias('mixxx-test', test_bin) + if default: + Default(test_bin) + + +# If the 'test' flag is 1, then build the mixxx-test target by default. If +# 'test' is in the target list then run mixxx-test. +build_tests_by_default = int(build.flags['test']) != 0 +build_tests = 'mixxx-test' in COMMAND_LINE_TARGETS +run_tests = 'test' in COMMAND_LINE_TARGETS +if build_tests or run_tests or build_tests_by_default: + define_test_targets(default=build_tests_by_default) + +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, distro_version): + # The format of a Debian/Ubuntu version is: + # + # [epoch:]upstream_version[-debian_revision] + # + # A detailed description of the valid characters and sorting order of + # versions can be found here: + # https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version + # + # For package upgrades to work correctly, we want the following + # orderings on package versions: + # + # nightly build < pre-alpha < alpha < beta < rc1 < rc2 < final release + # + # The sorting rules are complicated but the key detail is: "The lexical + # comparison is a comparison of ASCII values modified so that all the + # letters sort earlier than all the non-letters and so that a tilde + # sorts before anything, even the end of a part." + # + # The Mixxx version stored in src/defs_version.h (the "mixxx_version" + # parameter to this function) is formatted like: + # + # Pre Alpha: 2.0.0-alpha-pre + # Alpha: 2.0.0-alpha + # Beta: 2.0.0-beta + # RC: 2.0.0-rc1 + # Final: 2.0.0 + # + # Since hyphens are a separator character between the upstream version + # and Debian version, we replace these with tildes. + # + # Other goals: + # - We would like to know the branch and commit of a package. + # - We would like the PPA to trump the official Debian package. + # + # The following versions are sorted from low to high order: + # 1.9.9 + # 2.0.0~alpha~pre + # 2.0.0~alpha + # 2.0.0~beta~pre + # 2.0.0~beta + # 2.0.0~dfsg4 <- official Debian package version + # 2.0.0~rc1 + # 2.0.0 + # 2.0.1~alpha~pre + # + # Our official Debian packages have a ~dfsg section, so in this case an + # rc1 package in our PPA would trump an official Debian package + # (probably not what we want but not too bad since we would probably + # publish a "2.0.0" final to our PPA before the official Debian package + # is even released. + # + # Note in the above sorted list that if the branch name were included + # after the mixxx_version, 2.0.0~master would sort earlier than + # 2.0.0~rc1~master! To prevent branch and revision tags from + # interfering with package ordering we include them in the + # debian_revision portion of the version. This ensures they are only + # used for sorting if the upstream version of two packages is identical. + upstream_version = mixxx_version.replace('-', '~') + assert '_' not in upstream_version + + # Strip underscores and dashes in the branch name. + branch_name = branch_name.strip('_-') + assert branch_name and branch_name != '(no branch)' + + return "%s-%s~%s~%s%s~%s" % (upstream_version, ubuntu_version, branch_name, + vcs_name, vcs_revision, distro_version) + +#Set up the install target +#========================= + +#Mixxx binary +binary_files = [mixxx_bin]; +if test_bin is not None: + binary_files.append(test_bin) + +if build.bundle_pdbs: + binary_files.append(env.SideEffect('mixxx.pdb', mixxx_bin)) + +#Skins +skin_files = Glob('#res/skins/*') + +#Controller mappings +controllermappings_files = Glob('#res/controllers/*') + +# Translation files +translation_files = Glob('#res/translations/*.qm') + Glob(os.path.join(build.env['QTDIR'], 'translations/qt_*.qm')) + +# Font files +font_files = Glob('#res/fonts/*') + +#Keyboard mapping(s) +keyboardmappings_files = Glob('#res/keyboard/*') + +#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.svg') + +#Images for preferences dialog +image_files = Glob('#res/images/preferences/*') # These are compiled in to the "mixxx" binary through mixxx.qrc + +#Windows DLLs + +dll_files = [] +if build.toolchain_is_msvs and not build.static_dependencies: + # skip the MSVC DLLs in case they're in there too + dll_files.extend(Glob('%s/*.dll' % build.winlib_path)) + dll_files.extend(Glob('%s/lib/*.dll' % build.winlib_path)) + + if build.bundle_pdbs: + dll_files.extend(Glob('%s/*.pdb' % build.winlib_path)) + dll_files.extend(Glob('%s/lib/*.pdb' % build.winlib_path)) +elif build.crosscompile and build.platform_is_windows and build.toolchain_is_gnu and not build.static_dependencies: + # 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) + +if build.platform_is_windows: + suffix = 'd.dll' if build.build_is_debug else '.dll' + if not build.static_qt: + qt_modules = ['$QTDIR/lib/' + module.replace('Qt', 'Qt5') + suffix + for module in qt_modules] + dll_files.extend(qt_modules) + # https://doc.qt.io/qt-5/windows-deployment.html + # "If dynamic OpenGL is used, you additionally need to include the + # libraries required for ANGLE and software rendering. For ANGLE, both + # libEGL.dll and libGLESv2.dll from Qt's lib directory are required as + # well as the HLSL compiler from DirectX. The HLSL compiler library, + # d3dcompiler_XX.dll, where XX is the version number that ANGLE + # (libGLESv2) was linked against." + dll_files.extend(['$QTDIR/bin/libEGL' + suffix, + '$QTDIR/bin/libGLESv2' + suffix]) + d3dcompiler_path = util.find_d3dcompiler_dll(build.env) + if d3dcompiler_path: + dll_files.append(d3dcompiler_path) + +# Qt imageformats plugin +imgfmtdll_files = [] +qt_imagesformats = depends.Qt.enabled_imageformats(build) + +suffix = 'd.dll' if build.build_is_debug else '.dll' +if not build.static_qt: + imgfmtdll_files.extend(['$QTDIR/plugins/imageformats/' + module + suffix for module in qt_imagesformats]) +# We don't have Qt's dll pdb files in our release build environements, so only if build is debug +pdbSuffix = 'd.pdb' if (build.bundle_pdbs and build.build_is_debug) else '' +if pdbSuffix: + imgfmtdll_files.extend(['$QTDIR/plugins/imageformats/' + module + pdbSuffix for module in qt_imagesformats]) + +sqldll_files = [] +if int(flags.get('qt_sqlite_plugin', 0)): + # TODO(rryan): Add the SQLite DLL For Qt5. + pass + +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) + + 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) + + # 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', 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) + + if not building_debian_package and os.access(udev_root, os.W_OK): + env.Alias('install', hidudev) + + +#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" + + # In Qt 5, the SQLite driver was moved out of QtSql and into a plugin. + sql_dylibs = ["libqsqlite.dylib"] + + qt_plugins = ( + [("iconengines", e) for e in ["libqsvgicon.dylib"]] + + # Left out libqmng and libqtiff to save space. + [("imageformats", e) for e in + ["libqgif.dylib", "libqjpeg.dylib", "libqsvg.dylib"]] + + # Cocoa support moved to a plugin in Qt 5. + [("platforms", "libqcocoa.dylib")] + + [("sqldrivers", e) for e in sql_dylibs] + + [("styles", "libqmacstyle.dylib")] + ) + + resource_map = {} + for tfile in translation_files: + resource_map[str(tfile)] = 'translations' + + qtdir = build.env['QTDIR'] + qt_frameworks = depends.Qt.find_framework_libdir(qtdir) + 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(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 :( + 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, + MINIMUM_OSX_VERSION=util.get_osx_min_version(), + CATEGORY="public.app-category.music", + OTOOL_LOCAL_PATHS=otool_local_paths, + OTOOL_SYSTEM_PATHS=otool_system_paths, + FOR_APP_STORE=int(build.flags['macappstore']) > 0, + ) + 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) + #icon = env.Install(base_dist_dir+"", icon_files) + dlls = env.Install(base_dist_dir+"/", dll_files) + 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', docs) + env.Alias('mixxx', dlls) + #env.Alias('mixxx', icon) + env.Alias('mixxx', binary) + + binaries_to_codesign = [binary, dlls, vamp_plugins] + + # imageformats DLL + if imgfmtdll_files: + imageformats_dll = env.Install(os.path.join(base_dist_dir, "imageformats"), imgfmtdll_files) + binaries_to_codesign.append(imageformats_dll) + env.Alias('mixxx', imageformats_dll) + + # QSQLite DLL + if sqldll_files: + sql_dlls = env.Install(os.path.join(base_dist_dir, "sqldrivers"), sqldll_files) + binaries_to_codesign.append(sql_dlls) + env.Alias('mixxx', sql_dlls) + + if 'sign' in COMMAND_LINE_TARGETS: + codesign_subject_name = SCons.ARGUMENTS.get('windows_codesign_subject_name', '') + if not codesign_subject_name: + raise Exception('Code-signing was requested but windows_codesign_subject_name was not provided.') + codesign = env.SignTool( + 'Mixxx_signtool', + binaries_to_codesign, + SUBJECT_NAME=codesign_subject_name) + env.Alias('sign', codesign) + +def BuildRelease(target, source, env): + print("==== Mixxx Post-Build Checks ====") + print("You have built version %s" % mixxx_version) + if build.build_is_debug: + print("YOU ARE ABOUT TO PACKAGE A DEBUG BUILD!!") + print("Binary has size ", end='') + 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("Installer file ", end='') + package_name = 'mixxx' + + package_version = construct_version(build, mixxx_version, branch_name, + vcs_revision) + arch = "x64" if build.machine_is_64bit else "x86" + msi_name = '%s-%s-%s.msi' % (package_name, package_version, arch) + print(msi_name) + 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("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') + + #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 = 'vc_redist.%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(build.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...") + + print("Looking for WIX Toolset...") + wix_path = None + if not build.crosscompile and build.platform_is_windows: + wix_directory = os.getenv('WIX') + wix_path = '%s' % os.path.join(wix_directory, "bin") + elif build.crosscompile and build.platform_is_windows: + # TODO(XXX) How to handle that ? what does this exactly means ? + raise NotImplementedError + + if not wix_directory: + raise Exception ('Cannot find WIX Toolkit. Do you have it installed?') + else: + print(" Found Wix Toolset in " + wix_path) + + WinSDK_path = 'build\\wix' + + if not os.path.isfile(os.path.join(WinSDK_path, 'wisubstg.vbs')): + raise Exception ('can not find ' + WinSDK_path + '\wisubstg.vbs') + + if not os.path.isfile(os.path.join(WinSDK_path, 'WiLangId.vbs')): + raise Exception ('can not find ' + WinSDK_path + '\WiLangId.vbs') + + # Generating random ProductID (should change on every run) + # and put it in mixxx.wxs using the template + ProductID = str(uuid.uuid1()).upper() + with open("build/wix/ProductID.wxi.in", "rt") as fin: + with open("build/wix/ProductID.wxi", "wt") as fout: + for line in fin: + fout.write(line.replace('@PRODUCT_ID@', ProductID)) + fin.close() + fout.close() + + # The default language + defaultLanguage="en-us" + # The langIds contained in the installer. starting with LangId of the default language + langIds="1033" + + winArch = "x64" if build.machine_is_64bit else "x86" + + # Auto-create wxs file for each subdir and compile them + print("*** Building intermediate files") + for subdir in next(os.walk(dist_dir))[1]: + print(" " + dist_dir + "\\" + subdir) + # Exclude doc and imageformats helper DLLs, they are bundled elsewhere + if subdir in ['doc', 'imageformats']: + continue + command = '"%(wix)s\\heat.exe" dir %(distdir)s\%(sub)s -nologo -sfrag -suid -ag -srd -cg %(sub)sComp -dr %(sub)sDir -out build\wix\subdirs\%(sub)s.wxs -sw5150 -var var.%(sub)sVar' % \ + {'wix': wix_path, + 'distdir': dist_dir, + 'sub': subdir} + print("Using Command: " + command) + subprocess.check_call(command) + command = '"%(wix)s\\candle.exe" -nologo -dWINLIBPATH=%(winlibpath)s -dPlatform=%(arch)s -d%(sub)sVar=%(distdir)s\%(sub)s -arch %(arch)s -out build\wix\subdirs\%(sub)s.wixobj build\wix\subdirs\%(sub)s.wxs' % \ + {'wix': wix_path, + 'winlibpath': build.winlib_path, + 'arch': winArch, + 'distdir': dist_dir, + 'sub': subdir} + print("Using Command: " + command) + subprocess.check_call(command) + + # Handle QT's imageformats helper DLLs if dynamic QT + imageformats = "no" + if os.path.exists(os.path.join(dist_dir,"imageformats")) and not build.static_qt: + imageformats = "yes" + command = '"%(wix)s\\heat.exe" dir %(distdir)s\%(sub)s -nologo -sfrag -suid -ag -srd -cg %(sub)sComp -dr %(sub)sDir -out build\wix\subdirs\%(sub)s.wxs -sw5150 -var var.%(sub)sVar' % \ + {'wix': wix_path, + 'distdir': dist_dir, + 'sub': "imageformats"} + print("Using Command: " + command) + subprocess.check_call(command) + + command = '"%(wix)s\\candle.exe" -nologo -dWINLIBPATH=%(winlibpath)s -dPlatform=%(arch)s -d%(sub)sVar=%(distdir)s\%(sub)s -arch %(arch)s -out build\wix\subdirs\%(sub)s.wixobj build\wix\subdirs\%(sub)s.wxs' % \ + {'wix': wix_path, + 'winlibpath': build.winlib_path, + 'arch': winArch, + 'distdir': dist_dir, + 'sub': "imageformats"} + print("Using Command: " + command) + subprocess.check_call(command) + + # Harvest main DLL from install dir + command = '"%(wix)s\\heat.exe" dir %(distdir)s -nologo -sfrag -suid -ag -srd -cg mainDLLCompGroup -dr INSTALLDIR -out build\wix\subdirs\mainDLL.wxs -sw5150 -var var.SourceDir -t build\wix\only-dll.xslt' % \ + {'wix': wix_path, + 'distdir': dist_dir} + print("Using Command: " + command) + subprocess.check_call(command) + + command = '"%(wix)s\\candle.exe" -nologo -dWINLIBPATH=%(winlibpath)s -dPlatform=%(arch)s -dSourceDir=%(distdir)s -arch %(arch)s -out build\wix\subdirs\mainDLL.wixobj build\wix\subdirs\mainDLL.wxs' % \ + {'wix': wix_path, + 'winlibpath': build.winlib_path, + 'arch': winArch, + 'distdir': dist_dir} + print("Using Command: " + command) + subprocess.check_call(command) + + # Harvest main PDB from install dir if they exist + isPdb = "no" + if build.bundle_pdbs and glob.glob(os.path.join(dist_dir, "*.pdb")): + isPdb = "yes" + command = '"%(wix)s\\heat.exe" dir %(distdir)s -nologo -sfrag -suid -ag -srd -cg mainPDBCompGroup -dr INSTALLDIR -out build\wix\subdirs\mainPDB.wxs -sw5150 -var var.SourceDir -t build\wix\only-pdb.xslt' % \ + {'wix': wix_path, + 'distdir': dist_dir} + print("Using Command: " + command) + subprocess.check_call(command) + + command = '"%(wix)s\\candle.exe" -nologo -dWINLIBPATH=%(winlibpath)s -dPlatform=%(arch)s -dSourceDir=%(distdir)s -arch %(arch)s -out build\wix\subdirs\mainPDB.wixobj build\wix\subdirs\mainPDB.wxs' % \ + {'wix': wix_path, + 'winlibpath': build.winlib_path, + 'arch': winArch, + 'distdir': dist_dir} + print("Using Command: " + command) + subprocess.check_call(command) + + # Compile main wix files + command = '"%(wix)s\\candle.exe" -nologo -dWINLIBPATH=%(winlibpath)s -dPlatform=%(arch)s -dImageformats=%(isimageformats)s -dPDB=%(isPDB)s -arch %(arch)s -out build\wix\mixxx.wixobj build\wix\mixxx.wxs' % \ + {'wix': wix_path, + 'winlibpath': build.winlib_path, + 'isimageformats': imageformats, + 'isPDB': isPdb, + 'arch': winArch} + print("Using Command: " + command) + subprocess.check_call(command) + + # Build package for default language + print("*** Building package for default language " + defaultLanguage) + command = '"%(wix)s\\light.exe" -cc .\ -nologo -sw1076 -spdb -ext WixUIExtension -cultures:%(deflang)s -loc build\wix\Localization\mixxx_%(deflang)s.wxl -out %(package_name)s build\wix\*.wixobj build\wix\subdirs\*.wixobj' % \ + {'wix': wix_path, + 'deflang': defaultLanguage, + 'package_name': 'part.' + msi_name} + print("Using Command: " + command) + subprocess.check_call(command) + + bundlelocfile = open("build/wix/bundle/bundleloc.wxs", "w") + bundlelocfile.write("<?xml version='1.0' encoding='windows-1252'?>\n") + bundlelocfile.write("<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>\n") + bundlelocfile.write(" <Fragment Id='FragmentBundleLoc'>\n") + bundlelocfile.write(" <PayloadGroup Id='BundleLoc'>\n") + + for file in glob.glob('build\wix\Localization\mixxx_*.wxl'): + doc = minidom.parse(file) + wixloc = doc.getElementsByTagName("WixLocalization")[0] + culture = wixloc.getAttribute("Culture") + strings = doc.getElementsByTagName("String") + LCID = None + for string in strings: + if string.getAttribute('Id') == "Language": + LCID = string.firstChild.data + break + + if not LCID: + print("LCID not found, skipping file " + file) + continue + + bundlelocfile.write(" <Payload Id=\"thm-%(culture)s\" Compressed=\"yes\" Name=\"%(LCID)s\\thm.wxl\" SourceFile=\"..\\Localization\\mixxx_%(culture)s.wxl\" />\n" %\ + {'culture': culture, + 'LCID': LCID} + ) + + # Do not build localized MSI if it's default language + if culture == defaultLanguage: + continue + + print("*** Building package transform for locale %(culture)s LangID %(LCID)s" % \ + {'culture': culture, + 'LCID': LCID}) + + command = '"%(wix)s\\light.exe" -cc .\ -reusecab -nologo -sw1076 -spdb -ext WixUIExtension -cultures:%(lang)s,%(deflang)s -loc %(wxl_file)s -out %(lang)s.msi build\wix\*.wixobj build\wix\subdirs\*.wixobj' % \ + {'wix': wix_path, + 'lang': culture, + 'deflang': defaultLanguage, + 'wxl_file': file} + print("Using Command: " + command) + subprocess.check_call(command) + + command = '"%(wix)s\\torch.exe" -nologo -p -t language %(package_name)s %(lang)s.msi -o %(lang)s.mst' % \ + {'wix': wix_path, + 'lang': culture, + 'package_name': 'part.' + msi_name} + print("Using Command: " + command) + subprocess.check_call(command) + + command = 'cscript "%(winsdk)s\wisubstg.vbs" %(package_name)s %(lang)s.mst %(langid)s' % \ + {'winsdk': WinSDK_path, + 'lang': culture, + 'package_name': 'part.' + msi_name, + 'langid': LCID} + print("Using Command: " + command) + subprocess.check_call(command) + + langIds = langIds + "," + LCID + os.remove(culture + ".msi") + os.remove(culture + ".mst") + + print("*** Add all supported languages to MSI Package attribute") + command = 'cscript "%(winsdk)s\WiLangId.vbs" %(package_name)s Package %(langid)s' % \ + {'winsdk': WinSDK_path, + 'package_name': 'part.' + msi_name, + 'langid': langIds} + print("Using Command: " + command) + subprocess.check_call(command) + + bundlelocfile.write(" </PayloadGroup>\n") + bundlelocfile.write(" </Fragment>\n") + bundlelocfile.write("</Wix>\n") + bundlelocfile.close() + + # Everything is OK, now rename the msi to final name + if os.path.isfile(msi_name): + os.remove(msi_name) + os.rename('part.' + msi_name, msi_name) + + print("*** Compiling Bundle") + # Compile bundle wix file + command = '"%(wix)s\\candle.exe" -ext WixUtilExtension -ext WixBalExtension -nologo -dWINLIBPATH=%(winlibpath)s -dPlatform=%(arch)s -dMSIPackage=%(package_name)s -arch %(arch)s -out build\\wix\\bundle\\bundle.wixobj build\\wix\\bundle\\bundle.wxs' % \ + {'wix': wix_path, + 'winlibpath': build.winlib_path, + 'arch': winArch, + 'package_name': msi_name} + print("Using Command: " + command) + subprocess.check_call(command) + # bundle localisation references + command = '"%(wix)s\\candle.exe" -ext WixUtilExtension -ext WixBalExtension -nologo -dWINLIBPATH=%(winlibpath)s -dPlatform=%(arch)s -dMSIPackage=%(package_name)s -arch %(arch)s -out build\\wix\\bundle\\bundleloc.wixobj build\\wix\\bundle\\bundleloc.wxs' % \ + {'wix': wix_path, + 'winlibpath': build.winlib_path, + 'arch': winArch, + 'package_name': msi_name} + print("Using Command: " + comm |