#!/usr/bin/python3 import os import re import subprocess import shutil import sys import platform 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 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 error 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/platforms"): os.makedirs("app_dir/platforms") if not os.path.exists("app_dir/xcbglintegrations"): os.makedirs("app_dir/xcbglintegrations") if not os.path.exists("app_dir/multimedia"): os.makedirs("app_dir/multimedia") if not os.path.exists("app_dir/platformthemes"): os.makedirs("app_dir/platformthemes") if not os.path.exists("app_dir/lib"): os.makedirs("app_dir/lib") if not os.path.exists("app_dir/icons"): os.makedirs("app_dir/icons") verbose_copy(qt_bin + "/../plugins/platforms", "app_dir/platforms") verbose_copy(qt_bin + "/../plugins/xcbglintegrations", "app_dir/xcbglintegrations") verbose_copy(qt_bin + "/../plugins/multimedia", "app_dir/multimedia") verbose_copy(qt_bin + "/../plugins/platformthemes", "app_dir/platformthemes") verbose_copy("build/" + app_target, "app_dir/" + app_target) verbose_copy("icons/main.svg", "app_dir/icons/scalable.svg") img_sizes = [8, 16, 22, 24, 28, 32, 36, 42, 48, 64, 72, 96, 128, 192, 256, 512] for i in img_sizes: subprocess.run(["inkscape", "-w", str(i), "-h", str(i), "icons/main.svg", "-o", "app_dir/icons/" + str(i) + ".png"]) shutil.copyfile("build/" + 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 and "libc" not in line: #if ("libQt" in line) or ("libicu" 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/lib/" + file_name) if "/usr/lib/x86_64-linux-gnu/qt6/bin" == qt_bin: verbose_copy(qt_bin + "/../../libQt6DBus.so.6", "app_dir/lib/libQt6DBus.so.6") verbose_copy(qt_bin + "/../../libQt6XcbQpa.so.6", "app_dir/lib/libQt6XcbQpa.so.6") else: verbose_copy(qt_bin + "/../lib/libQt6DBus.so.6", "app_dir/lib/libQt6DBus.so.6") verbose_copy(qt_bin + "/../lib/libQt6XcbQpa.so.6", "app_dir/lib/libQt6XcbQpa.so.6") verbose_copy(qt_bin + "/../lib/libQt6OpenGL.so.6", "app_dir/lib/libQt6OpenGL.so.6") verbose_copy("templates/linux_run_script.sh", "app_dir/" + app_target + ".sh") verbose_copy("templates/linux_uninstall.sh", "app_dir/uninstall.sh") verbose_copy("templates/linux_icon.desktop", "app_dir/" + app_target + ".desktop") complete(app_ver, app_target) def complete(app_ver, app_target): 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 get_like_distro(): info = platform.freedesktop_os_release() ids = [info["ID"]] if "ID_LIKE" in info: # ids are space separated and ordered by precedence ids.extend(info["ID_LIKE"].split()) return ids def list_installed_packages(): like_distro = get_like_distro() if ("ubuntu" in like_distro) or ("debian" in like_distro) or ("linuxmint" in like_distro): return str(subprocess.check_output(["apt", "list", "--installed"]), 'utf-8') elif ("fedora" in like_distro) or ("rhel" in like_distro): return str(subprocess.check_output(["dnf", "list", "installed"]), 'utf-8') elif ("arch" in like_distro): return str(subprocess.check_output(["pacman", "-Q"]), 'utf-8') else: print("Warning: unable to determine a package manager for this platform.") return [] def list_of_words_in_text(list_of_words, text_body): for word in list_of_words: if not word in text_body: return False return True def platform_setup(): ins_packages = list_installed_packages() like_distro = get_like_distro() dep_pkgs_a = ["pkg-config"] dep_pkgs_b = ["ffmpeg", "libavcodec-dev", "libavformat-dev", "libavfilter-dev", "libavdevice-dev", "libilmbase-dev", "libvdpau-dev", "libxkbcommon-dev", "libgl-dev", "libxcb-cursor0", "inkscape"] if not list_of_words_in_text(dep_pkgs_a, ins_packages) or not list_of_words_in_text(dep_pkgs_b, ins_packages): if ("ubuntu" in like_distro) or ("debian" in like_distro) or ("linuxmint" in like_distro): subprocess.run(["sudo", "apt", "update", "-y"]) subprocess.run(["sudo", "apt", "install", "-y"] + dep_pkgs_a) subprocess.run(["sudo", "apt", "install", "-y"] + dep_pkgs_b) elif ("fedora" in like_distro) or ("rhel" in like_distro): subprocess.run(["sudo", "dnf", "install", "-y"] + dep_pkgs_a) subprocess.run(["sudo", "dnf", "install", "-y"] + dep_pkgs_b) elif ("arch" in like_distro): subprocess.run(["sudo", "pacman", "-S", "--noconfirm"] + dep_pkgs_a) subprocess.run(["sudo", "pacman", "-S", "--noconfirm"] + dep_pkgs_b) def main(): platform_setup() 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() if qt_bin != "": print("app_target = " + app_target) print("app_version = " + app_ver) print("app_name = " + app_name) print("qt_bin = " + qt_bin) cd() result = subprocess.run([qt_bin + os.sep + "qmake", "-config", "release"]) if result.returncode == 0: result = subprocess.run(["make"]) if result.returncode == 0: if os.path.exists("app_dir"): shutil.rmtree("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") linux_build_app_dir(app_ver, app_name, app_target, qt_bin) if __name__ == "__main__": main()