summaryrefslogtreecommitdiffstats
path: root/SConscript
diff options
context:
space:
mode:
authorRJ Skerry-Ryan <rryan@mixxx.org>2018-10-26 08:53:36 -0700
committerRJ Skerry-Ryan <rryan@mixxx.org>2018-10-26 08:53:36 -0700
commitd0e2d2d12d2b9853fa7aaba60928385df103fe23 (patch)
tree726e9fa36d10438f7fac4f0b22cbf3b9c1ed740d /SConscript
parentcfa1f632ee1ff827effc036eee10e0a7fb65907f (diff)
parent398b533a847db24ddb6f3d30e1d2964f62308cbf (diff)
Merge remote-tracking branch 'mixxxdj/master' into remove-vamp
Diffstat (limited to 'SConscript')
-rw-r--r--SConscript1158
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