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. 279
      utilities/install-lmk

279
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") INSTALL_SRC = os.path.join(HOME, "linux-minetest-kit-rsync", "mtkit", "minetest")
VARIANT = "rsync" 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;" MINETEST_KEYWORDS = ("sandbox;world;mining;crafting;blocks;nodes;multiplayer;"
"roleplaying;") "roleplaying;")
project_metas = { project_metas = {
'minetest': { # minetest is the project name (in mtsrc/newline dir) 'classic': { # minetest is the project name (in mtsrc/newline dir)
'shortcut': { 'shortcut': {
'GenericName': "Final Minetest", 'GenericName': "Final Minetest",
'Keywords': MINETEST_KEYWORDS, 'Keywords': MINETEST_KEYWORDS,
@ -109,10 +95,10 @@ project_metas = {
'Keywords': MINETEST_KEYWORDS+"minetest;", 'Keywords': MINETEST_KEYWORDS+"minetest;",
}, },
'dirname': "trolltest", 'dirname': "trolltest",
'name_and_variant_fmt': "Trolltest ({}) (minetest.org)", 'name_and_variant_fmt': "Trolltest ({}) (minetest.org build)",
'name': "Trolltest (minetest.org)", 'name': "Trolltest (minetest.org)",
'shortcut_exe_relpaths': [ 'shortcut_exe_relpaths': [
os.path.join("bin", "minetest"), os.path.join("bin", "trolltest"),
], ],
'platform_icon_relpath': { 'platform_icon_relpath': {
'Linux': os.path.join("misc", "minetest.svg"), '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): def write0(*args):
sys.stderr.write(*args) sys.stderr.write(*args)
sys.stderr.flush() sys.stderr.flush()
def echo0(*args): def echo0(*args):
print(*args, file=sys.stderr) print(*args, file=sys.stderr)
@ -138,18 +137,24 @@ def usage():
def main(): def main():
prefix = "[main] "
required_bin_suffixes = None 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 key_arg = None
install_from = None install_from = None
project_name = None
if len(sys.argv) < 2: if len(sys.argv) < 2:
usage() usage()
# if project_info is None: if project_meta is None:
echo0("Error: You must specify one of the names above.") 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 return 1
# TODO: maybe detect it: else:
# else: echo0("using detected project: {}".format(project_meta))
# echo0("using detected project: {}".format(project_info))
elif len(sys.argv) == 2: elif len(sys.argv) == 2:
pass # 1st arg (arg [1]) is always handled further down pass # 1st arg (arg [1]) is always handled further down
else: else:
@ -188,15 +193,76 @@ def main():
echo0("Error: {} must be followed by a value." echo0("Error: {} must be followed by a value."
"".format(key_arg)) "".format(key_arg))
return 1 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: if install_from is None:
install_from = INSTALL_SRC install_from = INSTALL_SRC
project_name = sys.argv[1]
results = install_minetest( 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, project_name,
pformat(project_meta.get('shortcut_exe_relpaths'))
)
)
results = install_minetest(
install_from, install_from,
required_bin_suffixes=required_bin_suffixes, project_meta,
) )
error = results.get('error') error = results.get('error')
if error is not None: if error is not None:
@ -204,20 +270,19 @@ def main():
return 0 return 0
def install_minetest(project_name, src, dst=None, variant_dirname=None, def install_minetest(src, project_meta, dst=None, variant_dirname=None,
required_bin_suffixes=[""], variant=VARIANT): variant=VARIANT):
"""Install Minetest """Install Minetest
Requires globals: Args:
project_meta (dict[string]): The information necessary project_meta (dict[string]): The information necessary
to install the program. It must have the keys: to install the program. It must have the keys:
- 'dirname' (string): The directory under the - 'dirname' (string): The directory under the
OS program files. 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 - There are more required keys for shortcut
generation (See install_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 src (string): The location of the minetest install source to
copy. copy.
dst (Optional[string]): Install here. If None, it will become 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 minetest-rsync). If VARIANT is blank or None, the
variant_dirname will become the same as the dirname variant_dirname will become the same as the dirname
(such as minetest). (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 variant (string): Append this to the dirname. It also
affects the shortcut--see "variant" under install_shortcut. 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: Returns:
dict: "destination" is where it was installed if at all. See dict: "destination" is where it was installed if at all. See
"warning" in case there was something incorrect about the "warning" in case there was something incorrect about the
install. install.
""" """
arg = project_name project_name = project_meta.get('name')
project_name = arg_to_project_name(arg) project_msg = project_name
if project_name is None: if project_msg is None:
usage() project_msg = pformat(project_meta)
return { del project_name
'error': "{} is not a valid project name.".format(arg),
} src_files = project_meta.get('required_relpaths')
src_files = expected_src_files(src, project_name, required_bin_suffixes)
if src_files is None: if src_files is None:
usage() 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) raise NotImplementedError(error)
missing_files = [] missing_files = []
for src_file in src_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) missing_files.append(src_file)
if len(missing_files) > 0: if len(missing_files) > 0:
error = ("Error: The following files are required to be compiled" error = ("Error: The following files are required to be compiled"
" {} before install but are not present: {}" " for {} before install but are missing: {}"
"".format(project_name, missing_files)) "".format(project_msg, missing_files))
return { return {
'error': error, 'error': error,
} }
project_meta = project_metas[project_name]
dirname = project_meta['dirname'] dirname = project_meta['dirname']
variant_dirname = dirname variant_dirname = dirname
if (variant is not None) and (len(variant.strip()) > 0): 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: else:
variant = None variant = None
if dst is None: if dst is None:
if platform.system() == "Windows": if platform.system() == "Windows":
GAMES = "C:\\games" 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) dst = os.path.join(HOME, variant_dirname)
warning = None warning = None
if not os.path.isdir(dst): 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) shutil.copytree(src, dst)
echo0("Done") echo0("Done")
result_path = dst result_path = dst
@ -295,6 +360,7 @@ def install_minetest(project_name, src, dst=None, variant_dirname=None,
# Leave result_path as None # Leave result_path as None
warning = 'Skipping installed "{}".'.format(dst) warning = 'Skipping installed "{}".'.format(dst)
echo0('WARNING: {}'.format(warning)) echo0('WARNING: {}'.format(warning))
for Exec_relpath in project_meta['shortcut_exe_relpaths']: for Exec_relpath in project_meta['shortcut_exe_relpaths']:
Exec = os.path.join(dst, Exec_relpath) Exec = os.path.join(dst, Exec_relpath)
sc_results = install_shortcut(Exec, dst, project_meta, variant) 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): def detect_project_meta(mt_share_path):
if arg == "final": """Detect the project info from a source *or* destination.
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
Only first entry will be used & get "server" added.
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 expected_src_files(src, project_name, required_bin_suffixes=[""]):
"""Get full paths of required files.
Args: Args:
required_bin_suffixes (list): Usually either [""] or ["server"], but if mt_share_path (string): The path containing
the list contains both, both will be required project_meta['shortcut_exe_relpaths']
filename(s).
Returns: 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) prefix = "[detect_project_meta] "
or (not isinstance(required_bin_suffixes, list))): matches = []
required_bin_suffixes = [""] for mode, meta in project_metas.items():
echo0('Warning: reverting to required_bin_suffixes=[""] since {}' new_meta = copy.deepcopy(meta)
''.format(required_bin_suffixes)) if 'required_relpaths' not in new_meta:
new_meta['required_relpaths'] = \
src_files = None meta['shortcut_exe_relpaths'].copy()
for sub in meta.get('shortcut_exe_relpaths'):
for appender in required_bin_suffixes: sub_path = os.path.join(mt_share_path, sub)
src_file = None try_extra_rel = sub+"server"
if project_name == "minetest": try_extra_exe = os.path.join(mt_share_path, try_extra_rel)
src_file = os.path.join(src, "bin", "minetest"+appender) found_any = False
elif project_name == "trolltest": if os.path.isfile(try_extra_exe):
src_file = os.path.join(src, "bin", "minetest"+appender) found_any = True
elif project_name == "finetest": if try_extra_rel not in new_meta['required_relpaths']:
src_file = os.path.join(src, "bin", "finetest"+appender) 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: else:
break echo0(prefix+"There is no %s" % try_extra_exe)
if src_file is not None: if os.path.isfile(sub_path):
if src_files is None: found_any = True
src_files = [] else:
src_files.append(src_file) echo0(prefix+"There is no %s" % sub_path)
# else caller must say project_name is not valid. new_meta['required_relpaths'].remove(sub)
return src_files # 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={}): def rewrite_conf(src, dst, changes={}):

Loading…
Cancel
Save