Browse Source

Detect the source type to avoid needing arguments when possible. Remove overlapping and non-dictionary metadata.

master
poikilos 1 year ago
parent
commit
48596f4b50
  1. 283
      utilities/install-lmk

283
utilities/install-lmk

@ -46,25 +46,11 @@ if sys.version_info.major < 3:
INSTALL_SRC = os.path.join(HOME, "linux-minetest-kit-rsync", "mtkit", "minetest")
VARIANT = "rsync"
mode_of_path = {
os.path.join("bin", "finetest"): {
'name': "finetest",
'appender': "",
'executable': "finetest",
},
os.path.join("bin", "finetestserver"): {
'name': "finetest",
'appender': "server",
'executable': "finetestserver",
},
# indeterminate if bin/minetest: same exe for classic & trolltest
}
MINETEST_KEYWORDS = ("sandbox;world;mining;crafting;blocks;nodes;multiplayer;"
"roleplaying;")
project_metas = {
'minetest': { # minetest is the project name (in mtsrc/newline dir)
'classic': { # minetest is the project name (in mtsrc/newline dir)
'shortcut': {
'GenericName': "Final Minetest",
'Keywords': MINETEST_KEYWORDS,
@ -109,10 +95,10 @@ project_metas = {
'Keywords': MINETEST_KEYWORDS+"minetest;",
},
'dirname': "trolltest",
'name_and_variant_fmt': "Trolltest ({}) (minetest.org)",
'name_and_variant_fmt': "Trolltest ({}) (minetest.org build)",
'name': "Trolltest (minetest.org)",
'shortcut_exe_relpaths': [
os.path.join("bin", "minetest"),
os.path.join("bin", "trolltest"),
],
'platform_icon_relpath': {
'Linux': os.path.join("misc", "minetest.svg"),
@ -124,11 +110,24 @@ project_metas = {
},
}
arg_project_name = {
# 'final': "classic",
'classic': "classic",
'trolltest': "trolltest",
# 'troll': "trolltest",
'finetest': "finetest",
# 'fine': "finetest",
}
for _name, _meta in project_metas.items():
_meta['project_name'] = _name
def write0(*args):
sys.stderr.write(*args)
sys.stderr.flush()
def echo0(*args):
print(*args, file=sys.stderr)
@ -138,18 +137,24 @@ def usage():
def main():
prefix = "[main] "
required_bin_suffixes = None
# project_info = detect_project_info(INSTALL_SRC)
why_meta = "detected"
project_meta = detect_project_meta(INSTALL_SRC)
if project_meta is None:
why_meta = "undetected"
key_arg = None
install_from = None
project_name = None
if len(sys.argv) < 2:
usage()
# if project_info is None:
echo0("Error: You must specify one of the names above.")
return 1
# TODO: maybe detect it:
# else:
# echo0("using detected project: {}".format(project_info))
if project_meta is None:
echo0("Error: You must specify one of the names above"
" unless well-known executable files can be detected"
" to determine what project is being installed.")
return 1
else:
echo0("using detected project: {}".format(project_meta))
elif len(sys.argv) == 2:
pass # 1st arg (arg [1]) is always handled further down
else:
@ -188,15 +193,76 @@ def main():
echo0("Error: {} must be followed by a value."
"".format(key_arg))
return 1
if required_bin_suffixes is None:
required_bin_suffixes = [""] # Always require at least the plain exe name.
if len(sys.argv) > 1:
name_arg = sys.argv[1]
project_name = arg_project_name.get(name_arg)
if project_name is None:
raise ValueError(
"Got %s but expected one from %s"
% (
pformat(name_arg),
pformat(list(arg_project_name.keys()))
)
)
if project_meta is not None:
echo0(prefix+"reverting detected meta due to %s argument."
% pformat(name_arg))
project_meta = None
why_meta = "cleared by %s argument" % name_arg
elif project_meta is not None:
project_name = project_meta.get('project_name')
# ^ May differ from name. For example, project name for
# Final Minetest is "classic".
echo0(prefix+"detected %s" % project_name)
if install_from is None:
install_from = INSTALL_SRC
project_name = sys.argv[1]
if project_meta is None:
if project_name is None:
raise ValueError(
"You must either specify one of %"
" or the source must be a well-known project that can be"
" detected." % pformat(list(project_metas.keys()))
)
project_meta = project_metas[project_name]
project_meta['required_relpaths'] = []
if required_bin_suffixes is None:
required_bin_suffixes = [""] # only check for * not *server
# when no options were specified.
echo0("Warning: No --client or --server option was set, and"
" source was %s so only client binary will be verified"
" to exist."
% why_meta)
for relpath in project_meta['shortcut_exe_relpaths']:
for suffix in required_bin_suffixes:
# for each file such as suffix "" for minetest and
# suffix "server" for minetestserver, add to required
# files if specified (Instead of if exists, which
# only is behavior on detect, though in both cases
# they are verified to exist before install, later).
try_relpath = relpath + suffix
project_meta['required_relpaths'].append(try_relpath)
echo0("Generated relpaths: %s" % pformat(project_meta['required_relpaths']))
else:
if project_meta.get('required_relpaths') is None:
raise NotImplementedError(
"Project %s was detected but required_relpaths was not set."
% pformat(project_meta.get('project_name'))
)
if len(project_meta['required_relpaths']) == 0:
raise FileNotFoundError(
"None of the well-known executables for %s could be found: %s"
% (
project_name,
pformat(project_meta.get('shortcut_exe_relpaths'))
)
)
results = install_minetest(
project_name,
install_from,
required_bin_suffixes=required_bin_suffixes,
project_meta,
)
error = results.get('error')
if error is not None:
@ -204,20 +270,19 @@ def main():
return 0
def install_minetest(project_name, src, dst=None, variant_dirname=None,
required_bin_suffixes=[""], variant=VARIANT):
def install_minetest(src, project_meta, dst=None, variant_dirname=None,
variant=VARIANT):
"""Install Minetest
Requires globals:
Args:
project_meta (dict[string]): The information necessary
to install the program. It must have the keys:
- 'dirname' (string): The directory under the
OS program files.
- 'required_files' (list): Paths relative to
src that are required (for ensuring src is intact).
- There are more required keys for shortcut
generation (See install_shortcut).
Args:
project_name (string): Must be minetest, finetest, or trolltest.
src (string): The location of the minetest install source to
copy.
dst (Optional[string]): Install here. If None, it will become
@ -229,44 +294,45 @@ def install_minetest(project_name, src, dst=None, variant_dirname=None,
minetest-rsync). If VARIANT is blank or None, the
variant_dirname will become the same as the dirname
(such as minetest).
required_bin_suffixes (list): Install client if [""], or server if
["server"]. Include both to require that both are in
os.path.join(src, "bin").
variant (string): Append this to the dirname. It also
affects the shortcut--see "variant" under install_shortcut.
On desktops environments following the XDG standard,
also appended to the icon filename so the variant's can
co-exist with other variants (such as deb and AppImage and
so on).
Returns:
dict: "destination" is where it was installed if at all. See
"warning" in case there was something incorrect about the
install.
"""
arg = project_name
project_name = arg_to_project_name(arg)
if project_name is None:
usage()
return {
'error': "{} is not a valid project name.".format(arg),
}
src_files = expected_src_files(src, project_name, required_bin_suffixes)
project_name = project_meta.get('name')
project_msg = project_name
if project_msg is None:
project_msg = pformat(project_meta)
del project_name
src_files = project_meta.get('required_relpaths')
if src_files is None:
usage()
error = "There are no source files for {}".format(project_name)
error = ("There are no specified source files for %s"
" so whether it is intact can't be checked."
"" % pformat(project_msg))
raise NotImplementedError(error)
missing_files = []
for src_file in src_files:
if not os.path.isfile(src_file):
if not os.path.isfile(os.path.join(src, src_file)):
missing_files.append(src_file)
if len(missing_files) > 0:
error = ("Error: The following files are required to be compiled"
" {} before install but are not present: {}"
"".format(project_name, missing_files))
" for {} before install but are missing: {}"
"".format(project_msg, missing_files))
return {
'error': error,
}
project_meta = project_metas[project_name]
dirname = project_meta['dirname']
variant_dirname = dirname
if (variant is not None) and (len(variant.strip()) > 0):
@ -274,7 +340,6 @@ def install_minetest(project_name, src, dst=None, variant_dirname=None,
else:
variant = None
if dst is None:
if platform.system() == "Windows":
GAMES = "C:\\games"
@ -285,9 +350,9 @@ def install_minetest(project_name, src, dst=None, variant_dirname=None,
dst = os.path.join(HOME, variant_dirname)
warning = None
if not os.path.isdir(dst):
write0('Installing {} to "{}"...'.format(project_name, dst))
write0('Installing %s to %s...'
% (pformat(project_msg), pformat(dst)))
shutil.copytree(src, dst)
echo0("Done")
result_path = dst
@ -295,6 +360,7 @@ def install_minetest(project_name, src, dst=None, variant_dirname=None,
# Leave result_path as None
warning = 'Skipping installed "{}".'.format(dst)
echo0('WARNING: {}'.format(warning))
for Exec_relpath in project_meta['shortcut_exe_relpaths']:
Exec = os.path.join(dst, Exec_relpath)
sc_results = install_shortcut(Exec, dst, project_meta, variant)
@ -433,68 +499,65 @@ def install_shortcut(Exec, dst, project_meta, variant):
# """
def arg_to_project_name(arg):
if arg == "final":
return "minetest"
elif arg == "classic":
return "minetest"
elif arg == "minetest":
return "minetest"
elif arg == "trolltest":
return "trolltest"
elif arg == "troll":
return "trolltest"
elif arg == "finetest":
return "finetest"
elif arg == "fine":
return "finetest"
return None
def detect_project_info(mt_share_path):
"""Detect the project name from a source *or* destination.
"""
for sub, project_info in mode_of_path.items():
sub_path = os.path.join(mt_share_path, sub)
if os.path.isfile(sub_path):
return project_info
return None
def detect_project_meta(mt_share_path):
"""Detect the project info from a source *or* destination.
def expected_src_files(src, project_name, required_bin_suffixes=[""]):
"""Get full paths of required files.
Only first entry will be used & get "server" added.
Args:
required_bin_suffixes (list): Usually either [""] or ["server"], but if
the list contains both, both will be required
mt_share_path (string): The path containing
project_meta['shortcut_exe_relpaths']
filename(s).
Returns:
list: Files that are required for install to continue.
A copy of the matching project_meta (from project_metas) with
an added entry 'required_relpaths'.
- *detecting errors*: If the list length is 0 or the key is not
present, no required files were found and the install source
is not understandable (There is no known binary such as for
different code to make a shortcut).
- If relpath+"server" exists in the case of the first entry in
'shortcut_exe_relpaths', that server binary will be added to
the 'required_relpaths' list whether or not the filename
without "server" in the name exists.
"""
if ((required_bin_suffixes is None) or (len(required_bin_suffixes) < 1)
or (not isinstance(required_bin_suffixes, list))):
required_bin_suffixes = [""]
echo0('Warning: reverting to required_bin_suffixes=[""] since {}'
''.format(required_bin_suffixes))
src_files = None
for appender in required_bin_suffixes:
src_file = None
if project_name == "minetest":
src_file = os.path.join(src, "bin", "minetest"+appender)
elif project_name == "trolltest":
src_file = os.path.join(src, "bin", "minetest"+appender)
elif project_name == "finetest":
src_file = os.path.join(src, "bin", "finetest"+appender)
else:
break
if src_file is not None:
if src_files is None:
src_files = []
src_files.append(src_file)
# else caller must say project_name is not valid.
return src_files
prefix = "[detect_project_meta] "
matches = []
for mode, meta in project_metas.items():
new_meta = copy.deepcopy(meta)
if 'required_relpaths' not in new_meta:
new_meta['required_relpaths'] = \
meta['shortcut_exe_relpaths'].copy()
for sub in meta.get('shortcut_exe_relpaths'):
sub_path = os.path.join(mt_share_path, sub)
try_extra_rel = sub+"server"
try_extra_exe = os.path.join(mt_share_path, try_extra_rel)
found_any = False
if os.path.isfile(try_extra_exe):
found_any = True
if try_extra_rel not in new_meta['required_relpaths']:
new_meta['required_relpaths'].append(try_extra_exe)
# For example, bin/minetestserver is required
# if in --server install mode (and this
# function detects that mode)
# but there is no shortcut to it in the GUI.
else:
echo0(prefix+"There is no %s" % try_extra_exe)
if os.path.isfile(sub_path):
found_any = True
else:
echo0(prefix+"There is no %s" % sub_path)
new_meta['required_relpaths'].remove(sub)
# For example, remove "minetest" if not present (but
# install can still proceed if "minetestserver" was
# added to the required list).
if found_any:
matches.append(new_meta)
break # only first entry will be used & get "server" added
if len(matches) == 1:
echo0(prefix+"found source files: %s" % pformat(matches[0]['required_relpaths']))
return matches[0]
return None
def rewrite_conf(src, dst, changes={}):

Loading…
Cancel
Save