MRCI/build.py

309 lines
11 KiB
Python
Raw Permalink Normal View History

#!/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_VER) +(\"(.*?)\")', 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_plugin_path():
try:
return str(subprocess.check_output(["qtpaths", "--plugin-dir"]), 'utf-8').strip()
except:
print("A direct call to 'qtpaths' has failed so automatic retrieval of the QT plugins folder is not possible.")
return input("Please enter the QT plugins 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_qt_plug_from_cli():
for arg in sys.argv:
if arg == "-qt_plugin":
index = sys.argv.index(arg)
try:
return sys.argv[index + 1]
except:
return ""
return ""
Slimmed down and simplified host administering - I decided to remove the entire concept of a root user. Instead, the host initializes as a blank slate and it will be up to the host admin to create a rank 1 user via the new command line option "-add_admin" to do initial setup with. - There is no longer such a concept as a protected user. Meaning even the last rank 1 user in the host database is allowed to delete or modify the rank of their own account. To prevent permanent "admin lock out" in this scenario the "-elevate" command line option was created. - Host settings are no longer stored in the database. Instead, host settings are now stored in a conf.json file in /etc/mrci/conf.json if running on a linux based OS or in %Programdata%\mrci\conf.json if running on Windows. - Email templates are no longer stored in the database. Instead, the templates can be any file formatted in UTF-8 text stored in the host file system. The files they point to can be modified in the conf.json file. - The conf file also replaced all use env variables so MRCI_DB_PATH, MRCI_WORK_DIR, MRCI_PRIV_KEY and MRCI_PUB_KEY are no longer in use. SSL/TLS cert paths can be modified in the conf file. - Removed email template cmds set_email_template and preview_email. - Also removed cmds close_host, host_config and restart_host. The actions these commands could do is best left to the host system command line. - The database class will now explicitly check for write permissions to the database and throw an appropriate error message if the check fails. "DROP TABLE" SQL abilities were added to make this happen. - Removed async cmds exit(3), maxses(5) and restart(11).
2020-11-10 14:47:00 -05:00
def get_info_header():
current_dir = os.path.dirname(__file__)
if current_dir == "":
Slimmed down and simplified host administering - I decided to remove the entire concept of a root user. Instead, the host initializes as a blank slate and it will be up to the host admin to create a rank 1 user via the new command line option "-add_admin" to do initial setup with. - There is no longer such a concept as a protected user. Meaning even the last rank 1 user in the host database is allowed to delete or modify the rank of their own account. To prevent permanent "admin lock out" in this scenario the "-elevate" command line option was created. - Host settings are no longer stored in the database. Instead, host settings are now stored in a conf.json file in /etc/mrci/conf.json if running on a linux based OS or in %Programdata%\mrci\conf.json if running on Windows. - Email templates are no longer stored in the database. Instead, the templates can be any file formatted in UTF-8 text stored in the host file system. The files they point to can be modified in the conf.json file. - The conf file also replaced all use env variables so MRCI_DB_PATH, MRCI_WORK_DIR, MRCI_PRIV_KEY and MRCI_PUB_KEY are no longer in use. SSL/TLS cert paths can be modified in the conf file. - Removed email template cmds set_email_template and preview_email. - Also removed cmds close_host, host_config and restart_host. The actions these commands could do is best left to the host system command line. - The database class will now explicitly check for write permissions to the database and throw an appropriate error message if the check fails. "DROP TABLE" SQL abilities were added to make this happen. - Removed async cmds exit(3), maxses(5) and restart(11).
2020-11-10 14:47:00 -05:00
return "src" + os.sep + "common.h"
else:
Slimmed down and simplified host administering - I decided to remove the entire concept of a root user. Instead, the host initializes as a blank slate and it will be up to the host admin to create a rank 1 user via the new command line option "-add_admin" to do initial setup with. - There is no longer such a concept as a protected user. Meaning even the last rank 1 user in the host database is allowed to delete or modify the rank of their own account. To prevent permanent "admin lock out" in this scenario the "-elevate" command line option was created. - Host settings are no longer stored in the database. Instead, host settings are now stored in a conf.json file in /etc/mrci/conf.json if running on a linux based OS or in %Programdata%\mrci\conf.json if running on Windows. - Email templates are no longer stored in the database. Instead, the templates can be any file formatted in UTF-8 text stored in the host file system. The files they point to can be modified in the conf.json file. - The conf file also replaced all use env variables so MRCI_DB_PATH, MRCI_WORK_DIR, MRCI_PRIV_KEY and MRCI_PUB_KEY are no longer in use. SSL/TLS cert paths can be modified in the conf file. - Removed email template cmds set_email_template and preview_email. - Also removed cmds close_host, host_config and restart_host. The actions these commands could do is best left to the host system command line. - The database class will now explicitly check for write permissions to the database and throw an appropriate error message if the check fails. "DROP TABLE" SQL abilities were added to make this happen. - Removed async cmds exit(3), maxses(5) and restart(11).
2020-11-10 14:47:00 -05:00
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, qt_plug):
if not os.path.exists("app_dir/linux/sqldrivers"):
os.makedirs("app_dir/linux/sqldrivers")
if not os.path.exists("app_dir/linux/lib"):
os.makedirs("app_dir/linux/lib")
verbose_copy(qt_plug + "/sqldrivers/libqsqlite.so", "app_dir/linux/sqldrivers/libqsqlite.so")
verbose_copy(qt_plug + "/sqldrivers/libqsqlodbc.so", "app_dir/linux/sqldrivers/libqsqlodbc.so")
verbose_copy(qt_plug + "/sqldrivers/libqsqlpsql.so", "app_dir/linux/sqldrivers/libqsqlpsql.so")
verbose_copy(qt_plug + "/sqldrivers/libqsqlmysql.so", "app_dir/linux/sqldrivers/libqsqlmysql.so")
verbose_copy("build/linux/" + app_target, "app_dir/linux/" + app_target)
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):
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)
verbose_copy("templates/linux_run_script.sh", "app_dir/linux/" + app_target + ".sh")
verbose_copy("templates/linux_service.service", "app_dir/linux/" + app_target + ".service")
verbose_copy("templates/linux_uninstall.sh", "app_dir/linux/uninstall.sh")
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")
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\\windows_shtask.xml", "app_dir\\windows\\shtask.xml")
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():
Slimmed down and simplified host administering - I decided to remove the entire concept of a root user. Instead, the host initializes as a blank slate and it will be up to the host admin to create a rank 1 user via the new command line option "-add_admin" to do initial setup with. - There is no longer such a concept as a protected user. Meaning even the last rank 1 user in the host database is allowed to delete or modify the rank of their own account. To prevent permanent "admin lock out" in this scenario the "-elevate" command line option was created. - Host settings are no longer stored in the database. Instead, host settings are now stored in a conf.json file in /etc/mrci/conf.json if running on a linux based OS or in %Programdata%\mrci\conf.json if running on Windows. - Email templates are no longer stored in the database. Instead, the templates can be any file formatted in UTF-8 text stored in the host file system. The files they point to can be modified in the conf.json file. - The conf file also replaced all use env variables so MRCI_DB_PATH, MRCI_WORK_DIR, MRCI_PRIV_KEY and MRCI_PUB_KEY are no longer in use. SSL/TLS cert paths can be modified in the conf file. - Removed email template cmds set_email_template and preview_email. - Also removed cmds close_host, host_config and restart_host. The actions these commands could do is best left to the host system command line. - The database class will now explicitly check for write permissions to the database and throw an appropriate error message if the check fails. "DROP TABLE" SQL abilities were added to make this happen. - Removed async cmds exit(3), maxses(5) and restart(11).
2020-11-10 14:47:00 -05:00
with open(get_info_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()
qt_plug = get_qt_plug_from_cli()
if qt_bin == "":
qt_bin = get_qt_path()
if qt_plug == "":
if platform.system() == "Linux":
qt_plug = get_qt_plugin_path()
else:
qt_plug = "not-needed"
maker = get_maker(qt_bin)
if qt_bin != "" and qt_plug != "":
print("app_target = " + app_target)
print("app_version = " + app_ver)
print("app_name = " + app_name)
print("qt_bin = " + qt_bin)
print("qt_plugins = " + qt_plug)
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, qt_plug)
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()