diff --git a/trunk/PKGBUILD b/trunk/PKGBUILD index a07f5f7..e0338c4 100644 --- a/trunk/PKGBUILD +++ b/trunk/PKGBUILD @@ -9,20 +9,22 @@ arch=('x86_64') pkgdesc='XMPP client written in C++ with Qt and Swiften' url="http://swift.im/" license=('GPL3') -makedepends=('python2' 'openssl' 'boost' 'qt5-base' 'qt5-multimedia' +makedepends=('scons' 'openssl' 'libidn' 'boost' 'qt5-base' 'qt5-multimedia' 'qt5-webkit' 'qt5-x11extras' 'qt5-tools' 'qt5-svg') source=("http://swift.im/downloads/releases/$pkgbase-$pkgver/$pkgbase-$pkgver.tar.gz" 'swift-4.0.2-boost-1.69-compatibility.patch' 'swift-4.0.2-qt-5.11-compatibility.patch' 'swift-4.0.2-qt-5.15-compatibility.patch' repro-builds.patch - swift-gcc11.patch) + swift-gcc11.patch + swift-python3.patch) sha256sums=('1c7c9fb981ecb9b589890c36523dbbabde2f0a708f71a66eb71e9526bf8e0a70' 'd166362c146f859ec89c535f8676ac12c6e51e281e6c88c5e36b25e4ea5655d1' '7970844d39c38ff6d36c2d1e26c145495ff8f5a5a31a399467eca66854dc3e67' '18feef21870b0f441a6fb0faf7b35a826cfec4fe4a1d717bbb1c373a8c1ccb95' '90c586cc67dc9831a0e8ce75c12478f674b5d429dd46dae4c47f241d7902beb1' - 'eae6d270660353e0038dfbbdebff974e84e59cd44992ce7fc37add9926fb49c0') + 'eae6d270660353e0038dfbbdebff974e84e59cd44992ce7fc37add9926fb49c0' + '3ab07b1d936c9e3baf747825c083fb56599f965110cdfa615c60b16f3673b068') # Those options need to be consistent between each scons invocation. _scons_options=(max_jobs=1 optimize=1 debug=0 swiften_dll=1 cflags="${CPPFLAGS} ${CFLAGS}" cxxflags="${CPPFLAGS} ${CFLAGS}" linkflags="${LDFLAGS}") @@ -36,11 +38,12 @@ prepare() { patch -p1 -i ../repro-builds.patch # Fix build with GCC 11 patch -p1 -i ../swift-gcc11.patch + patch -p1 -i ../swift-python3.patch } build() { cd $pkgbase-$pkgver - ./scons "${_scons_options[@]}" Swift Swiften + scons "${_scons_options[@]}" Swift Swiften } package_swift-im() { @@ -49,7 +52,7 @@ package_swift-im() { 'qt5-x11extras' 'qt5-svg' 'libxss' 'hicolor-icon-theme') cd $pkgbase-$pkgver - ./scons "${_scons_options[@]}" SWIFT_INSTALLDIR="$pkgdir"/usr/ "$pkgdir"/usr/ + scons "${_scons_options[@]}" SWIFT_INSTALLDIR="$pkgdir"/usr/ "$pkgdir"/usr/ } package_swiften() { @@ -57,5 +60,5 @@ package_swiften() { depends=('boost-libs' 'libxml2' 'libidn' 'avahi' 'openssl') cd $pkgbase-$pkgver - ./scons "${_scons_options[@]}" SWIFTEN_INSTALLDIR="$pkgdir"/usr/ "$pkgdir"/usr/ + scons "${_scons_options[@]}" SWIFTEN_INSTALLDIR="$pkgdir"/usr/ "$pkgdir"/usr/ } diff --git a/trunk/build-fix.patch b/trunk/build-fix.patch deleted file mode 100644 index 01814ca..0000000 --- a/trunk/build-fix.patch +++ /dev/null @@ -1,32 +0,0 @@ -diff -wbBur swift-4.0.2/Swift/QtUI/UserSearch/QtUserSearchWindow.h swift-4.0.2.my/Swift/QtUI/UserSearch/QtUserSearchWindow.h ---- swift-4.0.2/Swift/QtUI/UserSearch/QtUserSearchWindow.h 2018-04-06 13:06:46.000000000 +0300 -+++ swift-4.0.2.my/Swift/QtUI/UserSearch/QtUserSearchWindow.h 2019-10-08 20:52:23.171475337 +0300 -@@ -9,6 +9,7 @@ - #include - - #include -+#include - - #include - -diff -wbBur swift-4.0.2/Swiften/Network/PlatformNATTraversalWorker.cpp swift-4.0.2.my/Swiften/Network/PlatformNATTraversalWorker.cpp ---- swift-4.0.2/Swiften/Network/PlatformNATTraversalWorker.cpp 2018-04-06 13:06:46.000000000 +0300 -+++ swift-4.0.2.my/Swiften/Network/PlatformNATTraversalWorker.cpp 2019-10-08 21:12:25.284754131 +0300 -@@ -157,7 +157,7 @@ - miniUPnPInterface = new MiniUPnPInterface(); - miniUPnPSupported = miniUPnPInterface->isAvailable(); - } -- SWIFT_LOG(debug) << "UPnP NAT traversal supported: " << miniUPnPSupported << std::endl; -+// SWIFT_LOG(debug) << "UPnP NAT traversal supported: " << miniUPnPSupported << std::endl; - if (miniUPnPSupported) { - return miniUPnPInterface; - } -@@ -168,7 +168,7 @@ - natPMPInterface = new NATPMPInterface(); - natPMPSupported = natPMPInterface->isAvailable(); - } -- SWIFT_LOG(debug) << "NAT-PMP NAT traversal supported: " << natPMPSupported << std::endl; -+// SWIFT_LOG(debug) << "NAT-PMP NAT traversal supported: " << natPMPSupported << std::endl; - if (natPMPSupported) { - return natPMPInterface; - } diff --git a/trunk/swift-python3.patch b/trunk/swift-python3.patch new file mode 100644 index 0000000..c3d5b20 --- /dev/null +++ b/trunk/swift-python3.patch @@ -0,0 +1,803 @@ +diff --git a/3rdParty/LibIDN/SConscript b/3rdParty/LibIDN/SConscript +index 90892b0..c99aff8 100644 +--- a/3rdParty/LibIDN/SConscript ++++ b/3rdParty/LibIDN/SConscript +@@ -40,13 +40,13 @@ if env.get("LIBIDN_BUNDLED", False) : + if conf.CheckFunc("stricmp") : + myenv.Append(CPPDEFINES = [("strcasecmp", "stricmp")]) + else : +- print "Error: Cannot find strcasecmp() or stricmp()" ++ print("Error: Cannot find strcasecmp() or stricmp()") + Exit(1) + if not conf.CheckFunc('strncasecmp') : + if conf.CheckFunc("strnicmp") : + myenv.Append(CPPDEFINES = [("strncasecmp", "strnicmp")]) + else : +- print "Error: Cannot find strncasecmp() or strnicmp()" ++ print("Error: Cannot find strncasecmp() or strnicmp()") + Exit(1) + conf.Finish() + +diff --git a/BuildTools/CheckHeaders.py b/BuildTools/CheckHeaders.py +index 79ff85c..752d257 100755 +--- a/BuildTools/CheckHeaders.py ++++ b/BuildTools/CheckHeaders.py +@@ -36,7 +36,7 @@ for line in file.readlines() : + continue + for forbiddenInclude, ignores in FORBIDDEN_INCLUDES : + if forbiddenInclude in line and len([x for x in ignores if x in filename]) == 0 : +- print "Found " + forbiddenInclude + " include in " + filename ++ print("Found " + forbiddenInclude + " include in " + filename) + foundBadHeaders = True + + sys.exit(foundBadHeaders) +diff --git a/BuildTools/CheckTranslations.py b/BuildTools/CheckTranslations.py +index 615f81f..0617fba 100755 +--- a/BuildTools/CheckTranslations.py ++++ b/BuildTools/CheckTranslations.py +@@ -41,10 +41,10 @@ for filename in os.listdir("Swift/Translations") : + translationText = getText(translation.childNodes) + translationPlaceholders = set(re.findall("%\d+%?", translationText)) + if translationPlaceholders != sourcePlaceholders : +- print "[Error] " + filename + ": Placeholder mismatch in translation '" + sourceText + "'" ++ print("[Error] " + filename + ": Placeholder mismatch in translation '" + sourceText + "'") + if not finished : +- print "[Warning] " + filename + ": Unfinished" ++ print("[Warning] " + filename + ": Unfinished") + if language not in desktop_generic_names and language != "en" : +- print "[Warning] GenericName field missing in desktop entry for " + language ++ print("[Warning] GenericName field missing in desktop entry for " + language) + if language not in desktop_comments and language != "en" : +- print "[Warning] Comment field missing in desktop entry for " + language ++ print("[Warning] Comment field missing in desktop entry for " + language) +diff --git a/BuildTools/Copyrighter.py b/BuildTools/Copyrighter.py +index a16050c..4c7bfeb 100755 +--- a/BuildTools/Copyrighter.py ++++ b/BuildTools/Copyrighter.py +@@ -8,7 +8,7 @@ CONTRIBUTOR_LICENSE = "mit" + LICENSE_DIR = "Documentation/Licenses" + + # The following regex parses license comment blocks and its part out of a complete source file. +-reParseLicenseCommentBlocks = re.compile(ur'(\/\*\n\s\*\sCopyright \(c\) (?P\d\d\d\d)(-(?P\d\d\d\d))? (?P[^\n\.]*)\.?\n.\* (?P[^\n]*)\n \* (?P[^\n]+)\n *\*\/)') ++reParseLicenseCommentBlocks = re.compile(r'(\/\*\n\s\*\sCopyright \(c\) (?P\d\d\d\d)(-(?P\d\d\d\d))? (?P[^\n\.]*)\.?\n.\* (?P[^\n]*)\n \* (?P[^\n]+)\n *\*\/)') + + class License : + def __init__(self, name, file) : +@@ -122,14 +122,14 @@ def check_copyright(filename, hints) : + return True + else : + if hints : +- print "Copyright block for " + copyrightSetting.author + " does not cover current year in: " + filename ++ print("Copyright block for " + copyrightSetting.author + " does not cover current year in: " + filename) + return False + if hints : +- print "Missing copyright block for " + copyrightSetting.author + " in: " + filename ++ print("Missing copyright block for " + copyrightSetting.author + " in: " + filename) + return False + else : + if hints : +- print "No copyright found in: " + filename ++ print("No copyright found in: " + filename) + return False + + def replace_data_in_file(filename, begin, end, replaceWith) : +@@ -140,7 +140,7 @@ def replace_data_in_file(filename, begin, end, replaceWith) : + + def set_or_update_copyright(filename) : + if check_copyright(filename, False) : +- print "No update required for file: " + filename ++ print("No update required for file: " + filename) + else : + copyrightBlocks = parse_file_new(filename) + username, email = get_userinfo() +@@ -161,7 +161,7 @@ def set_or_update_copyright(filename) : + replace_data_in_file(filename, lastBlock+1, lastBlock+1, "\n" + str(copyrightSetting)) + + def print_help() : +- print """Usage: ++ print("""Usage: + Copyrighter.py check-copyright $filename + Cheks for the existence of a copyright comment block. + +@@ -172,7 +172,7 @@ def print_help() : + A users license configuration can be set via the SWIFT_LICENSE_CONFIG environment variable + in the format "$copyright holder|$license", e.g. "Jane Doe|mit". Possible values for + $license are default, mit and gpl. +- """ ++ """) + + if sys.argv[1] == "check-copyright" : + file = sys.argv[2] +@@ -183,6 +183,6 @@ elif sys.argv[1] == "set-copyright" : + file = sys.argv[2] + set_or_update_copyright(file) + else : +- print "Unknown command: " + sys.argv[1] ++ print("Unknown command: " + sys.argv[1]) + print_help() + sys.exit(-1) +diff --git a/BuildTools/DocBook/SCons/XSLT.py b/BuildTools/DocBook/SCons/XSLT.py +index 38e36c5..6a40b62 100644 +--- a/BuildTools/DocBook/SCons/XSLT.py ++++ b/BuildTools/DocBook/SCons/XSLT.py +@@ -8,7 +8,7 @@ import xml.dom.minidom, os, os.path + def generate(env) : + def generate_actions(source, target, env, for_signature) : + if not env.has_key("XSLTSTYLESHEET") : +- raise SCons.Errors.UserError, "The XSLTSTYLESHEET construction variable must be defined" ++ raise SCons.Errors.UserError("The XSLTSTYLESHEET construction variable must be defined") + + # Process the XML catalog files + # FIXME: It's probably not clean to do an ENV assignment globally +diff --git a/BuildTools/FixIncludes.py b/BuildTools/FixIncludes.py +index 8984944..e532464 100755 +--- a/BuildTools/FixIncludes.py ++++ b/BuildTools/FixIncludes.py +@@ -153,12 +153,12 @@ for line in content[headerStart:headerEnd]: + headerGroups[headerType] = [line] + + if containsComplexPreprocessorDirectives: +- print "Cannot format headers containing preprocessor #if, #pragma, #define or #undef statements!" ++ print("Cannot format headers containing preprocessor #if, #pragma, #define or #undef statements!") + exit(1) + + if filename_base.endswith(".h"): + if not HeaderType.PRAGMA_ONCE in headerGroups: +- print "Missing #pragma once!" ++ print("Missing #pragma once!") + exit(2) + cleanHeaderFile(content, headerStart, headerEnd, headerGroups) + elif filename_base.endswith(".cpp") or filename_base.endswith(".mm"): +diff --git a/BuildTools/GenerateAppCastFeeds.py b/BuildTools/GenerateAppCastFeeds.py +index 8135134..e7493df 100755 +--- a/BuildTools/GenerateAppCastFeeds.py ++++ b/BuildTools/GenerateAppCastFeeds.py +@@ -148,6 +148,6 @@ writeAppcastFile(filename=os.path.join(args.outputFolder, "swift-testing-appcast + writeAppcastFile(filename=os.path.join(args.outputFolder, "swift-development-appcast-mac.xml"), + title="Swift Development Releases", + description="", +- regexPattern="^\d+(\.\d+)?(\.\d+)?(alpha)?(beta\d+)?(rc\d+)?(-dev\d+)?$", ++ regexPattern="^\d+(\.\d+)?(\.\d+)?(alpha\d*)?(beta\d+)?(rc\d+)?(-dev\d+)?$", + appcastURL=urlparse.urljoin(args.downloadsURL, "swift-development-appcast-mac.xml"), + releases=automaticReleases) +diff --git a/BuildTools/GetBuildVersion.py b/BuildTools/GetBuildVersion.py +index 70fdc5c..be7cc03 100755 +--- a/BuildTools/GetBuildVersion.py ++++ b/BuildTools/GetBuildVersion.py +@@ -16,6 +16,6 @@ if only_major : + if version_match : + print version_match.group(1) + else : +- print "0" ++ print("0") + else : + print Version.getBuildVersion(os.path.dirname(sys.argv[0]) + "/..", sys.argv[1]) +diff --git a/BuildTools/Gource/GetGravatars.py b/BuildTools/Gource/GetGravatars.py +index d1f40a4..17198aa 100755 +--- a/BuildTools/Gource/GetGravatars.py ++++ b/BuildTools/Gource/GetGravatars.py +@@ -5,7 +5,7 @@ import subprocess, os, sys, hashlib, urllib + GRAVATAR_URL = "http://www.gravatar.com/avatar/%(id)s?d=404" + + if len(sys.argv) != 2 : +- print "Usage: " + sys.argv[0] + " " ++ print("Usage: " + sys.argv[0] + " ") + sys.exit(-1) + + output_dir = sys.argv[1] +@@ -18,32 +18,32 @@ for line in p.stdout.readlines() : + authors[author_components[0]] = author_components[1] + p.stdin.close() + if p.wait() != 0 : +- print "Error" ++ print("Error") + sys.exit(-1) + + # Get & save the avatars + if not os.path.isdir(output_dir) : + os.makedirs(output_dir) + for email, name in authors.items() : +- print "Processing avatar for " + name + " <" + email + ">" ++ print("Processing avatar for " + name + " <" + email + ">") + filename = os.path.join(output_dir, name + ".png") + if os.path.isfile(filename) : +- print "-> Already there. Skipping." ++ print("-> Already there. Skipping.") + continue + + m = hashlib.md5() + m.update(email) + url = GRAVATAR_URL % {"id" : m.hexdigest()} +- print "- Downloading " + url ++ print("- Downloading " + url) + f = urllib.urlopen(url) + input = None + if f.getcode() == 200 : + input = f.read() + f.close() + if input : +- print "- Saving file " + filename ++ print("- Saving file " + filename) + f = open(filename, "w") + f.write(input) + f.close() + else : +- print "- No Gravatar found" ++ print("- No Gravatar found") +diff --git a/BuildTools/ProjectCopyrightSummary.py b/BuildTools/ProjectCopyrightSummary.py +index 6e2d824..f6b18a0 100755 +--- a/BuildTools/ProjectCopyrightSummary.py ++++ b/BuildTools/ProjectCopyrightSummary.py +@@ -17,7 +17,7 @@ def CopyrightNames(filename): + names = [] + with open(filename, 'r') as file: + data = file.read() +- p = re.compile(ur'\* Copyright.*\d\d\d\d (.*?)\.?$', re.MULTILINE) ++ p = re.compile(r'\* Copyright.*\d\d\d\d (.*?)\.?$', re.MULTILINE) + names = re.findall(p, data) + return names + +diff --git a/BuildTools/SCons/SConstruct b/BuildTools/SCons/SConstruct +index 6d63d5b..2650c59 100644 +--- a/BuildTools/SCons/SConstruct ++++ b/BuildTools/SCons/SConstruct +@@ -7,9 +7,9 @@ root = Dir("../..").abspath + + # Override SConscript to handle tests + oldSConscript = SConscript +-def SConscript(*arguments, **keywords) : +- if not keywords.get("test_only", False) or env["TEST"] : +- return apply(oldSConscript, arguments, keywords) ++def SConscript(*args, **kwargs) : ++ if not kwargs.get("test_only", False) or env["TEST"] : ++ return oldSConscript(*args, **kwargs) + env.SConscript = SConscript + + ################################################################################ +@@ -142,11 +142,11 @@ conf = Configure(conf_env, custom_tests = { + }) + + if not conf.CheckCXX() or not conf.CheckCC() : +- print "Error: You need a working compiler" ++ print("Error: You need a working compiler") + Exit(1) + + if not conf.CheckCpp11Support() : +- print "Error: You need a compiler with support for the C++11 standard" ++ print("Error: You need a compiler with support for the C++11 standard") + Exit(1) + + +@@ -170,7 +170,7 @@ if (not zlib_okay) and conf.CheckLib("z") : + if zlib_okay : + env["ZLIB_FLAGS"] = zlib_flags + elif not env.get("zlib_bundled_enable", True) : +- print "Error: Zlib not found and zlib_bundled_enable is false" ++ print("Error: Zlib not found and zlib_bundled_enable is false") + Exit(1) + else : + env["ZLIB_BUNDLED"] = True +@@ -238,7 +238,7 @@ if not env.get("boost_force_bundled") and allLibsPresent : + # FIXME: Remove this workaround when UUID is available in most distros + env["BOOST_BUNDLED_UUID_ONLY"] = True + elif not env.get("boost_bundled_enable", True) : +- print "Error: Boost not found and boost_bundled_enable is false" ++ print("Error: Boost not found and boost_bundled_enable is false") + Exit(1) + else : + env["BOOST_BUNDLED"] = True +@@ -373,7 +373,7 @@ if env.get("try_expat", True) and not env.get("HAVE_LIBXML",0) : + # Bundled expat + bundledExpat = False + if not env.get("HAVE_EXPAT", 0) and not env.get("HAVE_LIBXML", 0) : +- print "Expat or LibXML not found. Using bundled Expat" ++ print("Expat or LibXML not found. Using bundled Expat") + SConscript("#/3rdParty/Expat/SConscript") + env["HAVE_EXPAT"] = 1 + env["EXPAT_BUNDLED"] = True +@@ -424,10 +424,10 @@ if not env.get("HAVE_ICU", False) and not env.get("HAVE_LIBIDN", False) : + env["HAVE_LIBIDN"] = 1 + env["LIBIDN_BUNDLED"] = 1 + elif env.get("need_idn", True): +- print "Error: ICU and LIBIDN not found, and libidn_bundled_enable is false" ++ print("Error: ICU and LIBIDN not found, and libidn_bundled_enable is false") + Exit(1) + else: +- print "Proceeding without an IDN library because need_idn was false. This will break all internal binaries" ++ print("Proceeding without an IDN library because need_idn was false. This will break all internal binaries") + + # Unbound + if env["unbound"] : +@@ -514,7 +514,7 @@ if not env.get("lua_force_bundled", False) and conf.CheckLibWithHeader(env["lua_ + if lua_version > 0 : + env["LUA_FLAGS"]["LUA_VERSION"] = str(lua_version // 100) + "." + str(lua_version % 100) + else : +- print "Warning: Unable to determine Lua version. Not installing Lua libraries." ++ print("Warning: Unable to determine Lua version. Not installing Lua libraries.") + env["LUA_FLAGS"].update(lua_flags) + else : + env["LUA_BUNDLED"] = 1 +@@ -666,7 +666,7 @@ try : + except SCons.Errors.StopError: + env["HAVE_QT"] = False + except Exception as e: +- print "Info: %s" % str(e) ++ print("Info: %s" % str(e)) + env["HAVE_QT"] = False + + ################################################################################ +@@ -688,7 +688,7 @@ try: + if not env.GetOption("clean") and env.get("install_git_hooks", True) : + env.Install("#/.git/hooks", Glob("#/BuildTools/Git/Hooks/*")) + except TypeError: +- print "You seem to be using Swift in a Git submodule. Not installing hooks." ++ print("You seem to be using Swift in a Git submodule. Not installing hooks.") + + + ################################################################################ +@@ -703,6 +703,8 @@ if ARGUMENTS.get("replace_pragma_once", False) : + return path[i+1:] + + for actual_root, dirs, files in os.walk(root) : ++ dirs.sort() ++ files.sort() + if "3rdParty" in actual_root : + continue + for file in files : +@@ -729,14 +731,14 @@ if ARGUMENTS.get("dump_trace", False) : + # Modules + modules = [] + if os.path.isdir(Dir("#/3rdParty").abspath) : +- for dir in os.listdir(Dir("#/3rdParty").abspath) : ++ for dir in sorted(os.listdir(Dir("#/3rdParty").abspath)) : + full_dir = os.path.join(Dir("#/3rdParty").abspath, dir) + if not os.path.isdir(full_dir) : + continue + sconscript = os.path.join(full_dir, "SConscript") + if os.path.isfile(sconscript) : + modules.append("3rdParty/" + dir) +-for dir in os.listdir(Dir("#").abspath) : ++for dir in sorted(os.listdir(Dir("#").abspath)) : + full_dir = os.path.join(Dir("#").abspath, dir) + if not os.path.isdir(full_dir) : + continue +@@ -752,7 +754,7 @@ modules.append("QA") + env["PROJECTS"] = [m for m in modules if m not in ["Documentation", "QA", "SwifTools"] and not m.startswith("3rdParty")] + for stage in ["flags", "build"] : + env["SCONS_STAGE"] = stage +- SConscript(dirs = map(lambda x : root + "/" + x, modules)) ++ SConscript(dirs = list(map(lambda x : root + "/" + x, modules))) + + # SLOCCount + if ARGUMENTS.get("sloccount", False) : +@@ -764,9 +766,9 @@ if ARGUMENTS.get("sloccount", False) : + # Print summary + ################################################################################ + +-print +-print " Build Configuration" +-print " -------------------" ++print("") ++print(" Build Configuration") ++print(" -------------------") + + parsers = [] + if env.get("HAVE_LIBXML", 0): +@@ -775,14 +777,14 @@ if env.get("HAVE_EXPAT", 0): + parsers.append("Expat") + if env.get("EXPAT_BUNDLED", False) : + parsers.append("(Bundled)") +-print " Projects: " + ' '.join(env["PROJECTS"]) +-print "" +-print " XML Parsers: " + ' '.join(parsers) ++print(" Projects: " + ' '.join(env["PROJECTS"])) ++print("") ++print(" XML Parsers: " + ' '.join(parsers)) + +-print " TLS Support: " + (env.get("HAVE_OPENSSL",0) and "OpenSSL" or env.get("HAVE_SECURETRANSPORT",0) and "Secure Transport" or env.get("HAVE_SCHANNEL", 0) and "Schannel" or "Disabled") +-print " DNSSD Support: " + (env.get("HAVE_BONJOUR") and "Bonjour" or (env.get("HAVE_AVAHI") and "Avahi" or "Disabled")) +-print ++print(" TLS Support: " + (env.get("HAVE_OPENSSL",0) and "OpenSSL" or env.get("HAVE_SECURETRANSPORT",0) and "Secure Transport" or env.get("HAVE_SCHANNEL", 0) and "Schannel" or "Disabled")) ++print(" DNSSD Support: " + (env.get("HAVE_BONJOUR") and "Bonjour" or (env.get("HAVE_AVAHI") and "Avahi" or "Disabled"))) ++print("") + + if not GetOption("help") and not env.get("HAVE_OPENSSL", 0) and not env.get("HAVE_SCHANNEL", 0) and not env.get("HAVE_SECURETRANSPORT", 0): +- print "Error: A working TLS backend is required. Please check the documentation for more information." ++ print("Error: A working TLS backend is required. Please check the documentation for more information.") + Exit(1) +diff --git a/BuildTools/SCons/Tools/Flags.py b/BuildTools/SCons/Tools/Flags.py +index fe0cfcc..7e0a972 100644 +--- a/BuildTools/SCons/Tools/Flags.py ++++ b/BuildTools/SCons/Tools/Flags.py +@@ -4,7 +4,7 @@ def generate(env) : + def useFlags(env, flags) : + for flag in flags : + if flag in env : +- env[flag] = env[flag] + flags[flag] ++ env[flag] = list(env[flag]) + flags[flag] + else : + env[flag] = flags[flag] + env.AddMethod(useFlags, "UseFlags") +diff --git a/BuildTools/SCons/Tools/qt4.py b/BuildTools/SCons/Tools/qt4.py +index d5c14e2..c97e751 100644 +--- a/BuildTools/SCons/Tools/qt4.py ++++ b/BuildTools/SCons/Tools/qt4.py +@@ -137,23 +137,23 @@ class _Automoc: + out_sources = source[:] + + for obj in source: +- if isinstance(obj,basestring): # big kludge! +- print "scons: qt4: '%s' MAYBE USING AN OLD SCONS VERSION AND NOT CONVERTED TO 'File'. Discarded." % str(obj) ++ if isinstance(obj,str): # big kludge! ++ print("scons: qt4: '%s' MAYBE USING AN OLD SCONS VERSION AND NOT CONVERTED TO 'File'. Discarded." % str(obj)) + continue + if not obj.has_builder(): + # binary obj file provided + if debug: +- print "scons: qt: '%s' seems to be a binary. Discarded." % str(obj) ++ print("scons: qt: '%s' seems to be a binary. Discarded." % str(obj)) + continue + cpp = obj.sources[0] + if not splitext(str(cpp))[1] in cxx_suffixes: + if debug: +- print "scons: qt: '%s' is no cxx file. Discarded." % str(cpp) ++ print("scons: qt: '%s' is no cxx file. Discarded." % str(cpp) ) + # c or fortran source + continue + #cpp_contents = comment.sub('', cpp.get_contents()) + try: +- cpp_contents = cpp.get_contents() ++ cpp_contents = str(cpp.get_contents()) + except: continue # may be an still not generated source + h=None + for h_ext in header_extensions: +@@ -163,12 +163,12 @@ class _Automoc: + h = find_file(hname, (cpp.get_dir(),), env.File) + if h: + if debug: +- print "scons: qt: Scanning '%s' (header of '%s')" % (str(h), str(cpp)) ++ print("scons: qt: Scanning '%s' (header of '%s')" % (str(h), str(cpp))) + #h_contents = comment.sub('', h.get_contents()) +- h_contents = h.get_contents() ++ h_contents = str(h.get_contents()) + break + if not h and debug: +- print "scons: qt: no header for '%s'." % (str(cpp)) ++ print("scons: qt: no header for '%s'." % (str(cpp))) + if h and q_object_search.search(h_contents): + # h file with the Q_OBJECT macro found -> add moc_cpp + moc_cpp = env.Moc4(h) +@@ -176,14 +176,14 @@ class _Automoc: + out_sources.append(moc_o) + #moc_cpp.target_scanner = SCons.Defaults.CScan + if debug: +- print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp)) ++ print("scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp))) + if cpp and q_object_search.search(cpp_contents): + # cpp file with Q_OBJECT macro found -> add moc + # (to be included in cpp) + moc = env.Moc4(cpp) + env.Ignore(moc, moc) + if debug: +- print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)) ++ print("scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc))) + #moc.source_scanner = SCons.Defaults.CScan + # restore the original env attributes (FIXME) + objBuilder.env = objBuilderEnv +@@ -344,7 +344,7 @@ def generate(env): + else: + result.append(itemPath) + return result +- contents = node.get_contents() ++ contents = str(node.get_contents()) + includes = [included[1] for included in qrcinclude_re.findall(contents)] + qrcpath = os.path.dirname(node.path) + dirs = [included for included in includes if os.path.isdir(os.path.join(qrcpath,included))] +diff --git a/BuildTools/SCons/Tools/textfile.py b/BuildTools/SCons/Tools/textfile.py +index 89f8963..6c93f2a 100644 +--- a/BuildTools/SCons/Tools/textfile.py ++++ b/BuildTools/SCons/Tools/textfile.py +@@ -107,7 +107,7 @@ def _action(target, source, env): + # write the file + try: + fd = open(target[0].get_path(), "wb") +- except (OSError,IOError), e: ++ except (OSError,IOError) as e: + raise SCons.Errors.UserError("Can't write target file %s" % target[0]) + # separate lines by 'linesep' only if linesep is not empty + lsep = None +diff --git a/BuildTools/SCons/Tools/wix.py b/BuildTools/SCons/Tools/wix.py +index 907b6d9..2d46188 100644 +--- a/BuildTools/SCons/Tools/wix.py ++++ b/BuildTools/SCons/Tools/wix.py +@@ -15,7 +15,10 @@ def generate(env) : + + def WiX_IncludeScanner(source, env, path, arg): + wixIncludeRegexp = re.compile(r'^\s*\<\?include (\S+.wxs)\s*\?\>\S*', re.M) +- contents = source.get_contents() ++ if SCons.Util.PY3: ++ contents = source.get_contents().decode("utf8") ++ else: ++ contents = source.get_contents() + includes = wixIncludeRegexp.findall(contents) + return [ "" + include for include in includes ] + +diff --git a/BuildTools/SCons/Version.py b/BuildTools/SCons/Version.py +index f215a5d..23cd850 100644 +--- a/BuildTools/SCons/Version.py ++++ b/BuildTools/SCons/Version.py +@@ -1,26 +1,34 @@ ++from __future__ import print_function + import subprocess, os, datetime, re, os.path, sys, unittest + +-def getGitBuildVersion(root, project) : +- tag = git("describe --tags --exact --match \"" + project + "-*\"", root) +- if tag : +- return tag.rstrip()[len(project)+1:] +- tag = git("describe --tags --match \"" + project + "-*\"", root) +- if tag : +- m = re.match(project + "-(.*)-(.*)-(.*)", tag) +- if m : +- return m.group(1) + "-dev" + m.group(2) ++def gitDescribeToVersion(tag, tagPrefix): ++ m = re.match(r'^' + tagPrefix + r'-(.+?)(?:-(.+?)-(.+?))?$', tag) ++ if not m: ++ return None ++ result = m.group(1) ++ if m.group(2): ++ result += "-dev" + m.group(2) ++ return result ++ ++def getGitBuildVersion(root, tagPrefix) : ++ tag = git("describe --tags --match \"" + tagPrefix + "-*\"", root) ++ if tag: ++ return gitDescribeToVersion(tag, tagPrefix) + return None + +-def git(cmd, root) : ++def git(cmd, root): + full_cmd = "git " + cmd ++ # Would've used with .. as p, but that's not supported by Python 2.7 + p = subprocess.Popen(full_cmd, cwd=root, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=(os.name != "nt")) + gitVersion = p.stdout.read() +- # error = p.stderr.read() +- # if error: +- # print "Git error: " + error +- p.stdin.close() +- if p.wait() == 0 : +- return gitVersion ++ try: ++ p.stdin.close() ++ p.stdout.close() ++ p.stderr.close() ++ except: ++ pass ++ if p.wait() == 0: ++ return bytes(gitVersion).decode('utf-8') + return None + + def getBuildVersion(root, project) : +@@ -95,7 +103,7 @@ def convertToWindowsVersion(version): + return (major, minor, patch) + + # Test Windows version mapping scheme +-class convertToWindowsVersionTest(unittest.TestCase): ++class TestCases(unittest.TestCase): + def testWindowsVersionsAreDescending(self): + versionStringsWithOldVersions = [ + ("5.0rc11", None), +@@ -111,13 +119,13 @@ class convertToWindowsVersionTest(unittest.TestCase): + ("4.0rc5", (4, 0, 50000)), + ("4.0rc4", (4, 0, 40000)), + ("4.0rc3", (4, 0, 30000)), ++ ('4.0rc2-dev39', (4, 0, 20039)), + ('4.0rc2-dev34', (4, 0, 20034)), + ('4.0rc2-dev33', (4, 0, 20033)), + ('4.0rc2-dev31', (4, 0, 20031)), + ('4.0rc2-dev30', (4, 0, 20030)), + ('4.0rc2-dev29', (4, 0, 20029)), + ('4.0rc2-dev27', (4, 0, 20027)), +- ('4.0rc2-dev39', (4, 0, 20039)), + ('4.0rc2', (4, 0, 20000)), + ('4.0rc1', (4, 0, 10000)), + ('4.0beta2-dev203', (4, 0, 2203)), +@@ -163,7 +171,17 @@ class convertToWindowsVersionTest(unittest.TestCase): + ('3.0alpha-dev524', (3, 0, 524)), + ('3.0alpha-dev515', (3, 0, 515)), + ] +- windowsVersionMapping = list(map(lambda (x,y): (x, convertToWindowsVersion(x)), versionStringsWithOldVersions)) ++ prevParsed = None ++ prevOldVersion = None ++ for string, oldVersion in versionStringsWithOldVersions: ++ parsed = convertToWindowsVersion(string) ++ if prevOldVersion and oldVersion: ++ self.assertTrue(oldVersion <= prevOldVersion, "Old version %r must come before %r" % (oldVersion, prevOldVersion)) ++ if prevParsed: ++ self.assertTrue(parsed < prevParsed, "%s => %r must be smaller than %s => %r" % (string, parsed, prevString, prevParsed)) ++ prevString = string ++ prevParsed = parsed ++ prevOldVersion = oldVersion + + def testThatBetaIsHigherThanAlpha(self): + self.assertTrue(convertToWindowsVersion("3.0beta0") > convertToWindowsVersion("3.0alpha0")) +@@ -190,12 +208,24 @@ class convertToWindowsVersionTest(unittest.TestCase): + self.assertTrue(convertToWindowsVersion("3.0") > convertToWindowsVersion("3.0rc1")) + self.assertTrue(convertToWindowsVersion("3.0") > convertToWindowsVersion("3.0rc11")) + ++ def testGitDescribeToVersion(self): ++ self.assertEqual("5.0alpha2-dev100", gitDescribeToVersion("swift-5.0alpha2-100-g33d5f6571", "swift")) ++ self.assertEqual("5.0alpha2", gitDescribeToVersion("swift-5.0alpha2", "swift")) ++ self.assertEqual("5.0-dev1", gitDescribeToVersion("swift-5.0-1-g012312312", "swift")) ++ self.assertIsNone(gitDescribeToVersion("swiften-5.0-1-g012312312", "swift")) ++ self.assertIsNone(gitDescribeToVersion("quickly-5.0-1-g012312312", "swift")) ++ self.assertIsNone(gitDescribeToVersion("swift-", "swift")) ++ self.assertIsNone(gitDescribeToVersion("swift", "swift")) ++ + if __name__ == '__main__': + if len(sys.argv) == 1: + unittest.main() + elif len(sys.argv) == 2: +- print convertToWindowsVersion(sys.argv[1]) ++ if sys.argv[1] == "--git-build-version": ++ print(getGitBuildVersion(os.path.dirname(os.path.dirname(__file__)), "swift")) ++ else: ++ print(convertToWindowsVersion(sys.argv[1])) + sys.exit(0) + else: +- print "Error: Simply run the script without arguments or pass a single argument." ++ print("Error: Simply run the script without arguments or pass a single argument.") + sys.exit(-1) +diff --git a/Slimber/SConscript b/Slimber/SConscript +index a41fe0b..fa5aa53 100644 +--- a/Slimber/SConscript ++++ b/Slimber/SConscript +@@ -11,10 +11,10 @@ elif env["PLATFORM"] != "darwin" : + env["PROJECTS"].remove("Slimber") + + if not "Slimber" in env["PROJECTS"] and env["SCONS_STAGE"] == "flags" : +- print "Bonjour missing. Not building Slimber." ++ print("Bonjour missing. Not building Slimber.") + + if not env.get("HAVE_QT", False) and "Slimber" in env["PROJECTS"] : +- print "Qt missing. Not building Slimber." ++ print("Qt missing. Not building Slimber.") + env["PROJECTS"].remove("Slimber") + + if "Slimber" in env["PROJECTS"] : +diff --git a/Sluift/SConscript b/Sluift/SConscript +index de9cab6..af54ece 100644 +--- a/Sluift/SConscript ++++ b/Sluift/SConscript +@@ -3,7 +3,7 @@ import Version, os.path + Import(["env"]) + + if env["SCONS_STAGE"] == "build" and not GetOption("help") and not env.get("HAVE_LUA", 0) : +- print "Warning: Lua was not found. Sluift will not be built." ++ print("Warning: Lua was not found. Sluift will not be built.") + if "Sluift" in env["PROJECTS"] : + env["PROJECTS"].remove("Sluift") + +diff --git a/Swift/Packaging/SConscript b/Swift/Packaging/SConscript +index 3aa791f..556c596 100644 +--- a/Swift/Packaging/SConscript ++++ b/Swift/Packaging/SConscript +@@ -25,5 +25,5 @@ if env["SCONS_STAGE"] == "build" : + target='$HELP2MAN_DEBIAN_DIR/swift-im.1', source='#/Swift/QtUI/swift-im', + action = Action('$HELP2MAN --no-discard-stderr -m "Swift Manual" -S "Swift" -n "swift-im" -N $SOURCE > $TARGET', cmdstr = "$HELP2MANSTR")) + else: +- print "Enabled help2man but help2man is not in the PATH of the current environment." ++ print("Enabled help2man but help2man is not in the PATH of the current environment.") + Exit(1) +diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript +index 54f0450..3d4e3e8 100644 +--- a/Swift/QtUI/SConscript ++++ b/Swift/QtUI/SConscript +@@ -1,5 +1,6 @@ + import os, datetime, re, time + import Version ++import SCons.Util + + def generateQRCTheme(dir, prefix) : + sourceDir = dir.abspath +@@ -19,10 +20,10 @@ def generateQRCTheme(dir, prefix) : + + Import("env") + +-myenv = env.Clone() ++myenv = env.Clone(tools = [ 'textfile' ]) + + # Disable warnings that affect Qt +-myenv["CXXFLAGS"] = filter(lambda x : x != "-Wfloat-equal", myenv["CXXFLAGS"]) ++myenv["CXXFLAGS"] = list(filter(lambda x : x != "-Wfloat-equal", myenv["CXXFLAGS"])) + if "clang" in env["CC"] : + myenv.Append(CXXFLAGS = ["-Wno-float-equal", "-Wno-shorten-64-to-32", "-Wno-missing-prototypes", "-Wno-unreachable-code", "-Wno-disabled-macro-expansion", "-Wno-unused-private-field", "-Wno-extra-semi", "-Wno-duplicate-enum", "-Wno-missing-variable-declarations", "-Wno-conversion", "-Wno-undefined-reinterpret-cast"]) + +@@ -351,7 +352,7 @@ myenv["TEXTFILESUFFIX"] = "" + copying_files = [myenv.File("../../COPYING.gpl"), myenv.File("../../COPYING.thirdparty"), myenv.File("../../COPYING.dependencies")] + if env["PLATFORM"] == "darwin" and env["HAVE_SPARKLE"] : + copying_files.append(env["SPARKLE_COPYING"]) +-myenv.MyTextfile(target = "COPYING", source = copying_files, LINESEPARATOR = "\n\n========\n\n\n") ++myenv.Textfile(target = "COPYING", source = copying_files, LINESEPARATOR = "\n\n========\n\n\n") + + ################################################################################ + # Translation +@@ -461,11 +462,16 @@ if env["PLATFORM"] == "win32" : + #myenv.Nsis("../Packaging/nsis/swift.nsi") + if env["SCONS_STAGE"] == "build" and env.get("wix_bindir", None): + def convertToRTF(env, target, source) : +- infile = open(source[0].abspath, 'r') +- outfile = open(target[0].abspath, 'w') ++ if SCons.Util.PY3: ++ infile = open(source[0].abspath, 'r', encoding="utf8") ++ outfile = open(target[0].abspath, 'w', encoding="utf8") ++ else: ++ infile = open(source[0].abspath, 'r') ++ outfile = open(target[0].abspath, 'w') + outfile.write('{\\rtf1\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\\fs16\\f0\\pard\n') + for line in infile: +- for char in line.decode("utf-8") : ++ line = line if SCons.Util.PY3 else line.decode("utf-8") ++ for char in line : + if ord(char) > 127 : + # FIXME: This is incorrect, because it only works for latin1. + # The correct way is \u? , but this is more +@@ -496,16 +502,16 @@ if env["PLATFORM"] == "win32" : + def signToolAction(target = None, source = None, env = None): + signresult = 0 + for x in range (1, 4) : +- print "Attemping to sign the packages [%s]" % x ++ print("Attemping to sign the packages [%s]" % x) + signresult = env.Execute('signtool.exe sign /fd SHA256 /f "${SIGNTOOL_KEY_PFX}" /t "${SIGNTOOL_TIMESTAMP_URL}" /d "Swift Installer" ' + str(target[0])) + if signresult != 1 : + break + #If all 3 attemps to sign the package failed, stop the build. + if signresult == 1 : +- print "Error: The build has failed to sign the installer package" ++ print("Error: The build has failed to sign the installer package") + Exit(1) + if signresult == 2 : +- print "Signing was completed with warnings." ++ print("Signing was completed with warnings.") + + myenv.AddPostAction(lightTask, signToolAction) + +diff --git a/Swift/SConscript b/Swift/SConscript +index b211435..cb1560a 100644 +--- a/Swift/SConscript ++++ b/Swift/SConscript +@@ -7,17 +7,17 @@ SConscript("Controllers/SConscript") + if env["SCONS_STAGE"] == "build" : + if not GetOption("help") and not env.get("HAVE_QT", 0) : + if "Swift" in env["PROJECTS"] : +- print "Warning: Swift requires Qt. Not building the Swift Qt application." ++ print("Warning: Swift requires Qt. Not building the Swift Qt application.") + env["PROJECTS"].remove("Swift") + elif not GetOption("help") and env["target"] == "native" and "Swift" in env["PROJECTS"] : + try : + SConscript("QtUI/SConscript") + except Exception as e: +- print "Warning: %s" % str(e) ++ print("Warning: %s" % str(e)) + env["PROJECTS"].remove("Swift") + if "Swift" in env["PROJECTS"] and env["BOOST_1_64_DETECTED"] and not env.get("allow_boost_1_64") and not env.GetOption("clean") : + #Version 1.64 has some issues with the serialization of boost::optional, see https://svn.boost.org/trac10/ticket/13050 +- print "Boost 1.64 has been detected. It is not recommended to use this version due to a regression within the library. Swift has been removed from the current build. You can still use this version by setting allow_boost_1_64 to true, but recent chats and highlighting rules will reset." ++ print("Boost 1.64 has been detected. It is not recommended to use this version due to a regression within the library. Swift has been removed from the current build. You can still use this version by setting allow_boost_1_64 to true, but recent chats and highlighting rules will reset.") + env["PROJECTS"].remove("Swift") + if env["help2man"]: +- SConscript("Packaging/SConscript") +\ No newline at end of file ++ SConscript("Packaging/SConscript") +diff --git a/Swiften/SConscript b/Swiften/SConscript +index 334f448..beae30d 100644 +--- a/Swiften/SConscript ++++ b/Swiften/SConscript +@@ -12,7 +12,7 @@ external_swiften_dep_modules = ["BOOST"] + if env["SCONS_STAGE"] == "flags" : + env["SWIFTEN_DLL"] = env["swiften_dll"] + env["SWIFTEN_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift") +- version_match = re.match("(\d+)\.(\d+).*", env["SWIFTEN_VERSION"]) ++ version_match = re.match(r"(\d+)\.(\d+).*", env["SWIFTEN_VERSION"]) + if version_match : + env["SWIFTEN_VERSION_MAJOR"] = int(version_match.group(1)) + env["SWIFTEN_VERSION_MINOR"] = int(version_match.group(2))