#!/usr/bin/python3 import os import re import subprocess import shutil import platform import sys def get_app_target(text): return re.search(r'(APP_TARGET) +(\"(.*?)\")', text).group(3) def get_app_ver(text): return re.search(r'(APP_VERSION) +(\"(.*?)\")', text).group(3) def get_app_name(text): return re.search(r'(APP_NAME) +(\"(.*?)\")', text).group(3) def get_qt_path(): try: return str(subprocess.check_output(["qtpaths", "--binaries-dir"]), 'utf-8').strip() except: print("A direct call to 'qtpaths' has failed so automatic retrieval of the QT bin folder is not possible.") return input("Please enter the QT bin path (leave blank to cancel the build): ") def get_qt_from_cli(): for arg in sys.argv: if arg == "-qt_dir": index = sys.argv.index(arg) try: return sys.argv[index + 1] except: return "" return "" def get_ver_header(): current_dir = os.path.dirname(__file__) if current_dir == "": return "src" + os.sep + "common.h" else: return current_dir + os.sep + "src" + os.sep + "common.h" def get_nearest_subdir(path, sub_name): dir_list = os.listdir(path) ret = "" for entry in dir_list: if sub_name in entry: ret = entry break return ret def get_maker(qt_path): ret = "" if platform.system() == "Linux": ret = "make" elif platform.system() == "Windows": path = os.path.abspath(qt_path + "\\..") name = os.path.basename(path) if "mingw" in name: tools_path = os.path.abspath(qt_path + "\\..\\..\\..\\Tools") mingw_ver = name[5:7] mingw_tool_subdir = get_nearest_subdir(tools_path, "mingw" + mingw_ver) mingw_tool_path = tools_path + "\\" + mingw_tool_subdir + "\\bin" if not os.environ['PATH'].endswith(";"): os.environ['PATH'] = os.environ['PATH'] + ";" os.environ['PATH'] = os.environ['PATH'] + mingw_tool_path ret = "mingw32-make" elif "msvc" in name: print("Warning: this script will assume you already ran the VsDevCmd.bat or vsvars32.bat script files") print(" for Microsoft Visual Studio. Either way, a call to 'nmake' should be recognizable as ") print(" a shell command otherwise this script will fail.\n") ans = input("If that is the case enter 'y' to continue or any other key to cancel the build: ") if ans == 'y' or ans == 'Y': ret = "nmake" else: exit() else: print("The system platform is unknown. Output from platform.system() = " + platform.system()) return ret def cd(): current_dir = os.path.dirname(__file__) if current_dir != "": os.chdir(current_dir) def verbose_copy(src, dst): print("cpy: " + src + " --> " + dst) if os.path.isdir(src): if os.path.exists(dst) and os.path.isdir(dst): shutil.rmtree(dst) try: # ignore errors thrown by shutil.copytree() # it's likely not actually failing to copy # the directory but still throws errors if # it fails to apply the same file stats as # the source. this type of errors can be # ignored. shutil.copytree(src, dst) except: pass elif os.path.exists(src): shutil.copyfile(src, dst) else: print("wrn: " + src + " does not exists. skipping.") def linux_build_app_dir(app_ver, app_name, app_target, qt_bin): if not os.path.exists("app_dir/linux/platforms"): os.makedirs("app_dir/linux/platforms") if not os.path.exists("app_dir/linux/xcbglintegrations"): os.makedirs("app_dir/linux/xcbglintegrations") if not os.path.exists("app_dir/linux/lib"): os.makedirs("app_dir/linux/lib") if not os.path.exists("app_dir/icons"): os.makedirs("app_dir/icons") verbose_copy(qt_bin + "/../plugins/platforms/libqxcb.so", "app_dir/linux/platforms/libqxcb.so") verbose_copy(qt_bin + "/../plugins/xcbglintegrations/libqxcb-egl-integration.so", "app_dir/linux/xcbglintegrations/libqxcb-egl-integration.so") verbose_copy(qt_bin + "/../plugins/xcbglintegrations/libqxcb-glx-integration.so", "app_dir/linux/xcbglintegrations/libqxcb-glx-integration.so") verbose_copy("build/linux/" + app_target, "app_dir/linux/" + app_target) verbose_copy("icons", "app_dir/icons") shutil.copyfile("build/linux/" + app_target, "/tmp/" + app_target) # copying the executable file from the build folder to # temp bypasses any -noexe retrictions a linux file # system may have. there is a chance temp is also # restricted in this way but that kind of config is # rare. ldd will not run correctly with -noexe # enabled. lines = str(subprocess.check_output(["ldd", "/tmp/" + app_target]), 'utf-8').split("\n") os.remove("/tmp/" + app_target) for line in lines: if " => " in line: if ("libQt" in line) or ("libicu" in line) or ("libssl" in line) or ("libcrypto" in line) or ("libGL.so" in line) or ("libpcre16.so" in line) or ("libpcre.so" in line): if " (0x0" in line: start_index = line.index("> ") + 2 end_index = line.index(" (0x0") src_file = line[start_index:end_index] file_name = os.path.basename(src_file) verbose_copy(src_file, "app_dir/linux/lib/" + file_name) if "/usr/lib/x86_64-linux-gnu/qt5/bin" == qt_bin: verbose_copy(qt_bin + "/../../libQt5DBus.so.5", "app_dir/linux/lib/libQt5DBus.so.5") verbose_copy(qt_bin + "/../../libQt5XcbQpa.so.5", "app_dir/linux/lib/libQt5XcbQpa.so.5") verbose_copy(qt_bin + "/../../libQt6DBus.so.6", "app_dir/linux/lib/libQt6DBus.so.6") verbose_copy(qt_bin + "/../../libQt6XcbQpa.so.6", "app_dir/linux/lib/libQt6XcbQpa.so.6") else: verbose_copy(qt_bin + "/../lib/libQt5DBus.so.5", "app_dir/linux/lib/libQt5DBus.so.5") verbose_copy(qt_bin + "/../lib/libQt5XcbQpa.so.5", "app_dir/linux/lib/libQt5XcbQpa.so.5") verbose_copy(qt_bin + "/../lib/libQt6DBus.so.6", "app_dir/linux/lib/libQt6DBus.so.6") verbose_copy(qt_bin + "/../lib/libQt6XcbQpa.so.6", "app_dir/linux/lib/libQt6XcbQpa.so.6") verbose_copy(qt_bin + "/../lib/libQt6OpenGL.so.6", "app_dir/linux/lib/libQt6OpenGL.so.6") verbose_copy("templates/linux_run_script.sh", "app_dir/linux/" + app_target + ".sh") verbose_copy("templates/linux_uninstall.sh", "app_dir/linux/uninstall.sh") verbose_copy("templates/linux_icon.desktop", "app_dir/linux/" + app_target + ".desktop") complete(app_ver, app_target) def windows_build_app_dir(app_ver, app_name, app_target, qt_bin): if os.path.exists("release"): os.removedirs("release") if os.path.exists("debug"): os.removedirs("debug") if not os.path.exists("app_dir\\windows"): os.makedirs("app_dir\\windows") if not os.path.exists("app_dir\\icons"): os.makedirs("app_dir\\icons") verbose_copy("icons", "app_dir\\icons") verbose_copy("build\\windows\\" + app_target + ".exe", "app_dir\\windows\\" + app_target + ".exe") verbose_copy("templates\\windows_uninstall.bat", "app_dir\\windows\\uninstall.bat") verbose_copy("templates\\win_icon.vbs", "app_dir\\windows\\icon.vbs") os.chdir("app_dir\\windows\\") result = subprocess.run([qt_bin + "\\" + "windeployqt", app_target + ".exe"]) cd() if result.returncode == 0: complete(app_ver, app_target) def complete(app_ver, app_target): if os.path.exists("Makefile"): os.remove("Makefile") if os.path.exists("Makefile.Debug"): os.remove("Makefile.Debug") if os.path.exists("Makefile.Release"): os.remove("Makefile.Release") if os.path.exists("object_script." + app_target + ".Debug"): os.remove("object_script." + app_target + ".Debug") if os.path.exists("object_script." + app_target + ".Release"): os.remove("object_script." + app_target + ".Release") print("Build complete for version: " + app_ver) print("You can now run the install.py script to install onto this machine or create an installer.") def main(): with open(get_ver_header()) as file: text = file.read() app_target = get_app_target(text) app_ver = get_app_ver(text) app_name = get_app_name(text) qt_bin = get_qt_from_cli() if qt_bin == "": qt_bin = get_qt_path() maker = get_maker(qt_bin) if qt_bin != "": print("app_target = " + app_target) print("app_version = " + app_ver) print("app_name = " + app_name) print("qt_bin = " + qt_bin) print("maker = " + maker + "\n") if maker == "": print("Could not find a valid maker/compiler on this platform, unable to continue.") else: cd() if platform.system() == "Linux": if os.path.exists("build/linux"): shutil.rmtree("build/linux") elif platform.system() == "Windows": if os.path.exists("build/windows"): shutil.rmtree("build/windows") result = subprocess.run([qt_bin + os.sep + "qmake", "-config", "release"]) if result.returncode == 0: result = subprocess.run([maker]) if result.returncode == 0: if not os.path.exists("app_dir"): os.makedirs("app_dir") with open("app_dir" + os.sep + "info.txt", "w") as info_file: info_file.write(app_target + "\n") info_file.write(app_ver + "\n") info_file.write(app_name + "\n") if platform.system() == "Linux": linux_build_app_dir(app_ver, app_name, app_target, qt_bin) elif platform.system() == "Windows": windows_build_app_dir(app_ver, app_name, app_target, qt_bin) else: print("The platform you are running in is not compatible with the app_dir build out procedure.") print(" output from platform.system() = " + platform.system()) if __name__ == "__main__": main()