Compare commits

...

6 Commits

  1. 4
      install-minetest-build-deps.sh
  2. 18
      pyenliven/mtpatches.py
  3. 4
      pyenliven/mttexture.py
  4. 807
      utilities/install-lmk
  5. 144
      utilities/pull-lmk
  6. 10
      utilities/run-any

4
install-minetest-build-deps.sh

@ -44,6 +44,10 @@ echo "Current Options:"
echo "enable_postgres:$enable_postgres" echo "enable_postgres:$enable_postgres"
echo "enable_redis:$enable_redis" echo "enable_redis:$enable_redis"
echo "enable_leveldb:$enable_leveldb" echo "enable_leveldb:$enable_leveldb"
if [ "x$enable_postgres$enable_redis$enable_leveldb" = "xfalsefalsefalse" ]; then
echo "Usage:"
echo "$0 [redis] [postgres] [leveldb]"
fi
#if [ -f "`command -v minetest`" ]; then #if [ -f "`command -v minetest`" ]; then
#echo "* trying to remove any non-git (packaged) version first (Press Ctrl C to cancel)..." #echo "* trying to remove any non-git (packaged) version first (Press Ctrl C to cancel)..."
luajit_path="/usr/include/luajit-2.1" luajit_path="/usr/include/luajit-2.1"

18
pyenliven/mtpatches.py

@ -510,9 +510,9 @@ def check_if_head_files_applied(bases, head_parents, skip_missing=False,
echo0('Warning: There is no base_root "{}".'.format(base_root)) echo0('Warning: There is no base_root "{}".'.format(base_root))
continue continue
for head_parent in head_parents: for head_parent in head_parents:
echo0("\n# patches={}".format(head_parent)) echo0("\n# set parent={}".format(head_parent))
for head_sub in os.listdir(head_parent): for head_sub in os.listdir(head_parent):
echo0("## patch={}".format(head_sub)) echo0("## set group={}".format(head_sub))
# Identify each head folder as an overlay to "patch" a base. # Identify each head folder as an overlay to "patch" a base.
# *Ignore files* in each head parent! # *Ignore files* in each head parent!
@ -606,14 +606,14 @@ def check_if_head_files_applied(bases, head_parents, skip_missing=False,
parallel_head = game_patch_root parallel_head = game_patch_root
_, game_name = os.path.split(base_root) _, game_name = os.path.split(base_root)
parallel_base = base_root parallel_base = base_root
echo0("* Checking whether {} was applied to {} game" echo0("# Checking whether {} was applied to {} game"
"".format(head_sub, game_name)) "".format(head_sub, game_name))
elif modpack_patch_root is not None: elif modpack_patch_root is not None:
parallel_head = modpack_patch_root parallel_head = modpack_patch_root
_, modpack_name = os.path.split(modpack_patch_root) _, modpack_name = os.path.split(modpack_patch_root)
modpack_rel = find_modpack(base_root, modpack_name) modpack_rel = find_modpack(base_root, modpack_name)
if modpack_rel is None: if modpack_rel is None:
echo0("Error: {} was not found in {}" echo0("# Error: {} was not found in {}"
"".format(modpack_name, base_root)) "".format(modpack_name, base_root))
continue continue
if modpack_rel: if modpack_rel:
@ -621,7 +621,7 @@ def check_if_head_files_applied(bases, head_parents, skip_missing=False,
else: else:
# Must be "", so avoid join to avoid adding os.path.sep # Must be "", so avoid join to avoid adding os.path.sep
parallel_base = base_root parallel_base = base_root
echo0("* Checking whether {} was applied to" echo0("# Checking whether {} was applied to"
" {} modpack in {} game" " {} modpack in {} game"
"".format(head_sub, modpack_name, game_name)) "".format(head_sub, modpack_name, game_name))
elif mod_patch_root is not None: elif mod_patch_root is not None:
@ -629,7 +629,7 @@ def check_if_head_files_applied(bases, head_parents, skip_missing=False,
_, mod_name = os.path.split(mod_patch_root) _, mod_name = os.path.split(mod_patch_root)
mod_rel = find_mod(base_root, mod_name) mod_rel = find_mod(base_root, mod_name)
if mod_rel is None: if mod_rel is None:
echo0("Error: {} was not found in {}" echo0("# Error: {} was not found in {}"
"".format(mod_name, base_root)) "".format(mod_name, base_root))
continue continue
if modpack_rel: if modpack_rel:
@ -637,18 +637,18 @@ def check_if_head_files_applied(bases, head_parents, skip_missing=False,
else: else:
# Must be "", so avoid join to avoid adding os.path.sep # Must be "", so avoid join to avoid adding os.path.sep
parallel_base = base_root parallel_base = base_root
echo0("* Checking whether {} was applied to" echo0("# Checking whether {} was applied to"
" {} mod in {} game" " {} mod in {} game"
"".format(head_sub, mod_name, game_name)) "".format(head_sub, mod_name, game_name))
else: else:
echo0('Warning: Skipping unknown patch structure: "{}"' echo0('# Warning: Skipping unknown patch structure: "{}"'
''.format(head_sub)) ''.format(head_sub))
diffs = diff_only_head(parallel_base, parallel_head, diffs = diff_only_head(parallel_base, parallel_head,
log_level=-1) log_level=-1)
if len(diffs) > 0: if len(diffs) > 0:
summary['unfinished_patch_count'] += 1 summary['unfinished_patch_count'] += 1
echo0('* differs from patch "{}": {} file(s)' echo0('# differs from patch "{}": {} file(s)'
''.format(head_parent, len(diffs))) ''.format(head_parent, len(diffs)))
for diff in diffs: for diff in diffs:
summary['unpatched_file_count'] += 1 summary['unpatched_file_count'] += 1

4
pyenliven/mttexture.py

@ -33,7 +33,6 @@ import os
from collections import OrderedDict from collections import OrderedDict
from PIL import Image from PIL import Image
from pprint import pformat
def echo0(*args, **kwargs): def echo0(*args, **kwargs):
@ -179,7 +178,7 @@ def recompose_anim(options):
raise ValueError( raise ValueError(
"{} should be a(n) {} but got {} {}." "{} should be a(n) {} but got {} {}."
"".format(name, option_type_name, "".format(name, option_type_name,
type(value).__name__, pformat(value)) type(value).__name__, repr(value))
) )
framer = Framer() framer = Framer()
img = Image.open(options['path']) img = Image.open(options['path'])
@ -273,4 +272,3 @@ def main():
print('- & keep Lua same) with:') print('- & keep Lua same) with:')
print('-- name="{}"'.format(new_path)) print('-- name="{}"'.format(new_path))
return 0 return 0

807
utilities/install-lmk

@ -1,780 +1,43 @@
#!/usr/bin/env python #!/usr/bin/env python
""" # -*- coding: utf-8 -*-
Usage: # #!/usr/bin/python3
install-lmk <project name> [--from <built dir>] [options] # based on EnlivenMinetest/utilities/install-lmk
#
The available project names are: # The copy in hierosoft is relicensed by Poikilos (original author)
classic (or final or minetest) to install ~/minetest-rsync, # under license of hierosoft
finetest (or fine) to install ~/finetest-rsync '''
(for the game that ripped off Multicraft.org's name), or install-lmk
trolltest (or troll) to install ~/trolltest-rsync (based on MT5). -----------
Use any "minetest" folder under the current working directory
If the current directory is not ~/minetest-rsync, the suffix "local" to install or upgrade.
will be used for installed directories and shortcuts instead of rsync to
indicate you are using a downloaded copy (not using a copy obtained via Developers: If /opt/minebest/mtkit is present, that will be used.
rsync access to the build server).
See hierosoft.hminetestsrc documentation for more info.
Options: '''
--from <built dir> Install from this directory. Defaults to import re
"/opt/minebest/mtkit/minetest" (from home not opt
if using Windows).
--server Require a binary ending with "server" to be
present in built dir. Defaults to False.
--client Require a binary ending with "server" to be
present in built dir. Defaults to True, but False
if --server is used without --client.
"""
from __future__ import print_function
import copy
import json
import os
import platform
import shutil
import stat
import sys import sys
import tempfile import os
from pprint import pformat
if platform.system() == "Windows":
HOME = os.environ['USERPROFILE']
SHORTCUTS_DIR = os.path.join(HOME, "Desktop")
elif platform.system() == "Darwin":
HOME = os.environ['HOME']
SHORTCUTS_DIR = os.path.join(HOME, "Desktop")
else:
HOME = os.environ['HOME']
SHORTCUTS_DIR = os.path.join(HOME, ".local", "share", "applications")
if sys.version_info.major < 3:
FileNotFoundError = IOError
ModuleNotFoundError = ImportError
INSTALL_SRC = os.path.join("/opt", "minebest-rsync", "mtkit", "minetest")
if platform.system() == "Windows":
INSTALL_SRC = os.path.join(HOME, "minebest-rsync", "mtkit", "minetest")
# ^ Changed later if detected in current dir (in use_if_source).
DETECT_KIT_SUBDIRS = ["minetest", "mtsrc"] # Use via detect_source
# ^ First entry of DETECT_KIT_SUBDIRS has to be the new INSTALL_SRC!
VARIANT = "rsync" # ^ Changed to "local" if not in default INSTALL_SRC
# - "local" copy of linux-minetest-kit.zip is for end users
# - "rsync" copy from /opt/minebest/ is for maintainers
# (linux-minetest-kit.zip is built by /assemble/util/buildskipwin.sh
# and then must be manually extracted to /opt/minebest/mtkit)
MINETEST_KEYWORDS = ("sandbox;world;mining;crafting;blocks;nodes;multiplayer;"
"roleplaying;")
project_metas = {
'classic': { # minetest is the project name (in mtsrc/newline dir)
'shortcut': {
'GenericName': "Final Minetest",
'Keywords': MINETEST_KEYWORDS,
},
'dirname': "minetest",
'name_and_variant_fmt': "Final Minetest ({})",
'name': "Final Minetest",
'shortcut_exe_relpaths': [
os.path.join("bin", "minetest"),
],
'platform_icon_relpath': {
'Linux': os.path.join("misc", "minetest.svg"),
'Darwin': os.path.join("misc", "minetest-icon.icns"),
'Windows': os.path.join("misc", "minetest-icon-24x24.png"),
},
'shortcut_relpath': os.path.join("misc", "net.minetest.minetest.desktop"),
'shortcut_name_noext': "org.minetest.minetest",
},
'finetest': {
'shortcut': {
'GenericName': "Finetest",
'Keywords': MINETEST_KEYWORDS+"minetest;",
},
'dirname': "finetest",
'name_and_variant_fmt': "Finetest ({})",
'name': "Finetest",
'shortcut_exe_relpaths': [
# os.path.join("bin", "multicraft"),
os.path.join("bin", "finetest"),
],
'platform_icon_relpath': {
'Linux': os.path.join("misc", "multicraft-xorg-icon-128.png"),
'Darwin': os.path.join("misc", "minetest-icon.icns"),
'Windows': os.path.join("misc", "multicraft-xorg-icon-128.png"),
},
'shortcut_relpath': os.path.join("misc", "net.minetest.minetest.desktop"),
'shortcut_name_noext': "org.minetest.finetest",
},
'trolltest': {
'shortcut': {
'GenericName': "Trolltest",
'Keywords': MINETEST_KEYWORDS+"minetest;",
},
'dirname': "trolltest",
'name_and_variant_fmt': "Trolltest ({}) (minetest.org build)",
'name': "Trolltest (minetest.org)",
'shortcut_exe_relpaths': [
os.path.join("bin", "trolltest"),
],
'platform_icon_relpath': {
'Linux': os.path.join("misc", "minetest.svg"),
'Darwin': os.path.join("misc", "minetest-icon.icns"),
'Windows': os.path.join("misc", "minetest-icon-24x24.png"),
},
'shortcut_relpath': os.path.join("misc", "net.minetest.minetest.desktop"),
'shortcut_name_noext': "org.minetest.trolltest",
},
}
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)
def usage():
echo0(__doc__)
def detect_source(path):
"""Get a built minetest directory inside of path if present.
It must contain all DETECT_KIT_SUBDIRS for the subdirectory to be
detected.
Returns:
str: minetest subdirectory. If path does not have
all DETECT_KIT_SUBDIRS, result is None.
"""
for sub in DETECT_KIT_SUBDIRS:
sub_path = os.path.join(path, sub)
if not os.path.isdir(sub_path):
return None
return os.path.join(path, DETECT_KIT_SUBDIRS[0])
def use_if_source(path):
"""Use the path as INSTALL_SRC if it contains a minetest install.
See detect_source for details. A message is shown regarding the
status.
Affects globals:
- INSTALL_SRC
- VARIANT
Returns:
bool: True if is a source (even if INSTALL_SRC is already the
same).
"""
global INSTALL_SRC
global VARIANT
detected_src = detect_source(path)
if detected_src:
if detected_src != INSTALL_SRC:
echo0('Switching from "{}" to local copy:'
'\n "{}"'
''.format(INSTALL_SRC, detected_src))
INSTALL_SRC = detected_src
VARIANT = "local"
else:
echo0('Using standard source location (same as current dir):'
'\n "{}"'
''.format(INSTALL_SRC))
return True
else:
echo0('Using standard source location'
' (since current dir does not have both "mtsrc and "minetest"):'
'\n "{}"'
''.format(INSTALL_SRC))
return False
def main():
prefix = "[main] "
use_if_source(os.getcwd())
required_bin_suffixes = None
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_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(
json.dumps(project_meta, indent=2),
))
# NOTE: ^ shows name_and_variant_fmt with literal "{}" still
# (unavoidable without messing with it), so see
# "Name={}" further down for that output (Only possible
# after `variant` is set).
elif len(sys.argv) == 2:
pass # 1st arg (arg [1]) is always handled further down
else:
for argi in range(2, len(sys.argv)):
arg = sys.argv[argi]
if key_arg is not None:
if arg.startswith("--"):
usage()
echo0("Error: {} must be followed by a value but got {}."
"".format(key_arg, arg))
return 1
if key_arg == "--from":
install_from = arg
else:
usage()
echo0("Error: unknown argument {}".format(key_arg))
return 1
elif arg == "--server":
if required_bin_suffixes is None:
required_bin_suffixes = ["server"]
else:
required_bin_suffixes.append("server")
elif arg == "--client":
if required_bin_suffixes is None:
required_bin_suffixes = [""]
else:
required_bin_suffixes.append("")
elif arg == "--from":
key_arg = arg
else:
usage()
echo0('Error: The 2nd argument must be "server" or left out')
return 1
if key_arg is not None:
usage()
echo0("Error: {} must be followed by a value."
"".format(key_arg))
return 1
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
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(
install_from,
project_meta,
)
error = results.get('error')
if error is not None:
echo0("Error: %s" % error)
return 0
def install_minetest(src, project_meta, dst=None, variant_dirname=None,
variant=None):
"""Install Minetest
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).
src (string): The location of the minetest install source to
copy.
dst (Optional[string]): Install here. If None, it will become
the default. Defaults to variant_dirname under C:\games on
Windows, otherwise under HOME.
variant_dirname (Optional[string]): Set the install directory
name (ignored if dst is set). If None, it will become the
default. Defaults to project_name + "-" + VARIANT (such as
minetest-rsync). If VARIANT is blank or None, the
variant_dirname will become the same as the dirname
(such as minetest).
variant (str): 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). Defaults to VARIANT (which is set automatically to
"rsync" or "local" elsewhere).
Returns:
dict: "destination" is where it was installed if at all. See
"warning" in case there was something incorrect about the
install.
"""
if variant is None:
variant = VARIANT
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 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(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"
" for {} before install but are missing: {}"
"".format(project_msg, missing_files))
return {
'error': error,
}
dirname = project_meta['dirname']
variant_dirname = dirname
if (variant is not None) and (len(variant.strip()) > 0):
variant_dirname += "-" + variant
else:
variant = None
if dst is None:
if platform.system() == "Windows":
GAMES = "C:\\games"
if not os.path.isdir(GAMES):
os.mkdir(GAMES)
dst = os.path.join(GAMES, variant_dirname)
else:
dst = os.path.join(HOME, variant_dirname)
warning = None
if not os.path.isdir(dst):
write0('Installing %s to %s...'
% (pformat(project_msg), pformat(dst)))
shutil.copytree(src, dst)
version_path = project_meta.get('version_path')
if version_path and os.path.isfile(version_path):
version_name = os.path.basename(version_path)
shutil.copy(
version_path,
os.path.join(dst, version_name),
)
echo0("Done")
result_path = dst
else:
# 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)
sc_warning = sc_results.get('warning')
if sc_warning is not None:
if warning is not None:
warning += "; " + sc_warning
else:
warning = sc_warning
return {
'dst': dst,
'warning': warning,
}
def generate_caption(project_meta, variant):
"""Generate the icon caption.
Args:
project_meta (dict): The dict containing 'name' and
'name_and_variant_fmt' where 'name' is like
"Trolltest (minetest.org)", and 'name_and_variant_fmt' is
like 'Trolltest ({}) (minetest.org build)'.
"""
Name = project_meta['name']
if variant is not None:
name_and_variant_fmt = project_meta.get('name_and_variant_fmt')
if name_and_variant_fmt is not None:
Name = name_and_variant_fmt.format(variant)
else:
Name += " (" + project_meta['variant'] + ")" # raise if None
return Name
def install_shortcut(Exec, dst, project_meta, variant):
"""Install a shortcut to any program on any understood platform.
- sc_template_path is determined based on dst and shortcut_relpath
- sc_installed_path (path) is determined from OS and shortcut_name_noext
(and variant if not None).
- sc_template_path is read, Exec string is filled based on dst
(the selected destination where the program is installed)
then the resulting shortcut is saved to sc_installed_path
(only after temp file is complete).
Args:
Exec (string): The executable path where the shortcut
should point.
dst (string): The directory path where the program is
installed.
project_meta (dict): All metadata describing the program.
For this method, it must have the keys:
- 'name': The entire name (except variant) that
should be displayed as the shortcut's caption.
- 'name_and_variant_fmt': Should either be not
present or contain the name and the placeholder
"{}" where the variant should go. If not present,
" " and variant will be added to the end of Name.
- 'shortcut' (dict): contains:
- 'GenericName' (Optional[string]): A simplified
name for the program. If None, the GenericName
line will be removed from the shortcut. This
option is only for GNU/Linux systems or other
systems using XDG.
- 'Keywords' (Optional[string]): If None, Keywords
line will be removed from the shortcut. This
option is only for GNU/Linux systems or other
systems using XDG.
- 'shortcut_relpath': The location of an existing
shortcut file to use and modify.
- 'platform_icon_relpath' (dict[string]): A dict
where the key is platform.system() (Must have
at least 'Linux', 'Windows', *AND* 'Darwin')
and the value is the relative path from
dst to the icon image file.
variant (string): The special string to put in parenthesis
after the name to denote what kind of package or source was
used to obtain the program, such as "rsync" if a local
custom build, or more commonly "git", "deb", etc. If it is
an official binary archive, set this to "release". However,
if the package type (such as deb) is native to your distro,
set this to None to indicate it is the package supported
for your distro.
- Name is constructed using
project_meta['name_and_variant_fmt'] if present, otherwise
Name will be project_meta['name] + " (" + 'variant' + ")".
If variant is None, name is project_meta['name'].
Raises:
FileNotFoundError: If src does not exist.
"""
warning = None
Name = generate_caption(project_meta, variant)
echo0("Name={}".format(Name))
platform_icon_relpath = project_meta.get('platform_icon_relpath')
icon_relpath = None
if platform_icon_relpath is not None:
icon_relpath = platform_icon_relpath.get(platform.system())
if icon_relpath is None:
raise NotImplementedError(
"There is no platform icon for {}.".format(platform.system())
)
Icon = os.path.join(dst, icon_relpath)
shortcut_meta = copy.deepcopy(project_meta.get('shortcut'))
shortcut_meta['Name'] = Name
shortcut_meta['Exec'] = Exec
shortcut_meta['Icon'] = Icon
# ^ rewrite_conf *removes* any lines where value is None
if platform.system() == "Linux":
sc_template_path = os.path.join(dst, project_meta['shortcut_relpath'])
shortcut_name = "{}.{}.desktop".format(
project_meta['shortcut_name_noext'],
variant,
)
sc_installed_path = os.path.join(SHORTCUTS_DIR, shortcut_name)
if not os.path.isdir(SHORTCUTS_DIR):
os.makedirs(SHORTCUTS_DIR) # default mode is 511
write0('Installing icon to "{}"...'.format(sc_installed_path))
rewrite_conf(
sc_template_path,
sc_installed_path,
changes=shortcut_meta,
)
echo0("OK")
elif platform.system() == "Darwin":
shortcut_name = Name + ".command"
sc_installed_path = os.path.join(SHORTCUTS_DIR, shortcut_name)
with open(sc_installed_path) as stream:
stream.write('"%s"\n' % Exec)
# ^ Run the game & close Command Prompt immediately.
# ^ First arg is Command Prompt title, so leave it blank.
st = os.stat(sc_installed_path)
os.chmod(sc_installed_path, st.st_mode | stat.S_IXUSR)
# ^ same as stat.S_IEXEC: "Unix V7 synonym for S_IXUSR."
elif platform.system() == "Windows":
shortcut_name = Name + ".bat"
sc_installed_path = os.path.join(SHORTCUTS_DIR, shortcut_name)
with open(sc_installed_path) as stream:
stream.write('start "" "%s"\n' % Exec)
# ^ Run the game & close Command Prompt immediately.
# ^ First arg is Command Prompt title, so leave it blank.
else:
warning = ("Icon install isn't implemented for {}."
"".format(platform.system()))
return {
"warning": warning, # may be None
"destination": dst,
}
# def get_missing_subs(mt_share_path, subs):
# """Get a list of any missing files for a source *or* destination.
# """
def detect_project_meta(mt_share_path):
"""Detect the project info from a source *or* destination.
Only first entry will be used & get "server" added.
Args:
mt_share_path (string): The path containing
project_meta['shortcut_exe_relpaths']
filename(s).
Returns: scripts_dir = os.path.dirname(os.path.realpath(__file__))
A copy of the matching project_meta (from project_metas) with repo_dir = os.path.dirname(scripts_dir)
an added entry 'required_relpaths'. repos_dir = os.path.dirname(repo_dir)
- *detecting errors*: If the list length is 0 or the key is not try_other_repo = os.path.join(repos_dir, "hierosoft")
present, no required files were found and the install source good_h_flag = os.path.join(try_other_repo, "hierosoft", "__init__.py")
is not understandable (There is no known binary such as for if os.path.isfile(good_h_flag):
different code to make a shortcut). sys.path.insert(0, try_other_repo)
- If relpath+"server" exists in the case of the first entry in print("Using {}".format(try_other_repo), file=sys.stderr)
'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.
"""
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: else:
echo0(prefix+"There is no %s" % sub_path) print("No {}. Trying installed copy...".format(good_h_flag),
new_meta['required_relpaths'].remove(sub) file=sys.stderr)
# For example, remove "minetest" if not present (but
# install can still proceed if "minetestserver" was
# added to the required list).
mt_share_path = os.path.realpath(mt_share_path)
version_paths = [
os.path.join(mt_share_path, "release.txt"),
os.path.join(os.path.dirname(mt_share_path), "release.txt"),
]
for version_path in version_paths:
if os.path.isfile(version_path):
new_meta['version_path'] = version_path
version = None
with open(version_path, 'r') as stream:
for rawL in stream:
if version is None:
version = rawL.strip()
# ^ Use `for` to avoid Exception on empty file.
if version is None:
echo0('Warning: "{}" is empty!'.format(version_path))
continue
elif not version:
echo0('Warning: "{}" had a blank line not version'
''.format(version_path))
version = None
continue
if version:
new_meta['version'] = version
break
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={}):
"""Install a conf such as an XDG desktop shortcut with changes.
Args:
src (string): The conf file to read.
dst (string): The conf file to write or overwrite.
changes (dict): A set of values to change by name. For any value
that is None, the line will be removed!
"""
# This function is redefined further down in the case of Python 2.
fd, path = tempfile.mkstemp()
try:
with os.fdopen(fd, 'wb') as tmp:
# ^ ensure still exists when moving
write0("Generating temporary icon %s..." % path)
# NOTE: tmp.name is just some number (int)!
with open(src, "rb") as stream:
for rawL in stream:
signI = rawL.find(b'=')
# commentI = rawl.find(b'#')
if rawL.strip().startswith(b"#"):
tmp.write(rawL)
continue
if rawL.strip().startswith(b"["):
tmp.write(rawL)
continue
if signI < 0:
tmp.write(rawL)
continue
key_bytes = rawL[:signI].strip()
key = key_bytes.decode("utf-8")
value = changes.get(key)
if key not in changes:
# The value wasn't changed so write it as-is
# echo0("%s not in %s" % (key, changes))
tmp.write(rawL)
continue
if value is None:
echo0("%s was excluded from the icon" % key)
continue
line = "%s=%s\n" % (key, value)
tmp.write(line.encode("utf-8"))
shutil.copy(path, dst)
finally:
write0("removing tmp file...")
os.remove(path)
if sys.version_info.major < 3: # if os.path.isfile(os.path.join(repos_dir, "hierosoft", "init.py")):
# Python 2 (strings are bytes) # sys.path.insert(repos_dir)
def rewrite_conf(src, dst, changes={}): # ^ implies both are installed (and that repos_dir is actually modules_dir),
"""Install a conf such as an XDG desktop shortcut with changes. # so leave path alone.
"""
fd, path = tempfile.mkstemp()
try:
with os.fdopen(fd, 'wb') as tmp:
write0("Generating temporary icon %s..." % path)
with open(src, "rb") as stream:
for rawL in stream:
signI = rawL.find('=')
# commentI = rawl.find('#')
if rawL.strip().startswith("#"):
tmp.write(rawL)
continue
if rawL.strip().startswith("["):
tmp.write(rawL)
continue
if signI < 0:
tmp.write(rawL)
continue
key_bytes = rawL[:signI].strip()
key = key_bytes
value = changes.get(key)
if key not in changes:
# The value wasn't changed so write it as-is
tmp.write(rawL)
continue
if value is None:
echo0("%s was excluded from the icon" % key)
continue
line = "%s=%s\n" % (key, value)
tmp.write(line)
shutil.copy(path, dst)
finally:
write0("removing tmp file...")
os.remove(path)
from hierosoft.hminetestsrc import main # noqa E402
if __name__ == "__main__": if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main()) sys.exit(main())

144
utilities/pull-lmk

@ -1,44 +1,116 @@
#!/bin/bash #!/bin/bash
rsync -rtlv mtio:/opt/minebest/ /opt/minebest \ OFFLINE=false
--exclude "mtkit/minetest/" \ for var in "$@"
--exclude "mtkit0/" \ do
--exclude "git-trees.save/" \ if [ "x$var" == "x--offline" ]; then
--exclude "tmp/" \ OFFLINE=true
--exclude "mtkit/" \ fi
done
if [ -d "/opt/rjkbin" ]; then
echo "Error: This should *not* be run on the server that has the master copy."
exit 1
fi
if [ "x$OFFLINE" = "xfalse" ]; then
rsync -rtlv --delete mtio:/opt/minebest/ /opt/minebest \
--include "assemble/***" \
--include "mtbin/***" \
--include "mtcache/***" \
--include "mtkit/***" \
--include "mtkit.txt" \
--exclude "*" \
; ;
fi
# l: copy symlinks as symlinks
# L: transform symlink into referent file/dir
# except this one (master copy on build server points to /var/mtworlds!):
if [ -L /opt/minebest/mtworlds ]; then rm /opt/minebest/mtworlds; fi
# Unimportant:
# --include "mtworlds/" \
# --include "mxekit/" \
# --include "pagecache/" \
# --include "tmp/" \
# Should get rebuilt after sync (automated further down):
# --include "prod/" \
# --exclude "mtkit0/" \
# --exclude "mtkit.old/" \
# --exclude "git-trees.save/" \
# --exclude "tmp/" \
# --exclude "mtkit/" \
# ^ Had too many exclusions, so include explicitly instead:
# ^ exclude mtkit because it is downstream from linux-minetest-kit.zip # ^ exclude mtkit because it is downstream from linux-minetest-kit.zip
# in prod (symlink to assemble/prod)! # in prod (symlink to assemble/prod)!
# (mtkit is the installed copy in the case of a server, and is used as # (mtkit is the installed copy in the case of a server, and is used as
# the install source when using the maintainer rsync copy as the # the install source when using the maintainer rsync copy as the
# install source) # install source)
code=$? code=$?
if [ $code -eq 0 ]; then
echo "[pull-lmk] * downloading /opt/minebest...OK"
else
echo "[pull-lmk] * downloading /opt/minebest...FAILED"
exit $code
fi
echo
echo
echo "[pull-lmk] * building /opt/minebest/prod..."
# mtkit
# - is the INSTALLED copy,
# - maybe OLDER than linux-minetest-kit.zip!
# echo "[pull-lmk] - removing prod..."
# rm -Rvf /opt/minebest/prod
if [ $? -ne 0 ]; then exit 1; fi
echo "[pull-lmk] - building prod..."
# NOTE: If zip files in newline in linux-minetest-kit were not up to
# date, update them using bash -e util/buildarch.sh
# which builds them from the git-trees subdirectories
# util/buildarch.sh
# Build /opt/minebest/prod:
cd /opt/minebest/assemble/ && PROTECTED_GROUP=owner PROTECTED_USER=owner bash -e util/buildskipwin.sh
code=$?
# prod
# - delete prod/* to test building prod using "bash -e buildskipwin.sh"
if [ $code -eq 0 ]; then
echo "[pull-lmk] * building /opt/minebest/prod...OK"
else
echo "[pull-lmk] * building /opt/minebest/prod...FAILED"
echo "[pull-lmk] * However, ignore "
# exit $code
fi
if [ -L /opt/minebest/mtkit ]; then if [ -L /opt/minebest/mtkit ]; then
rm -f /opt/minebest/mtkit rm -f /opt/minebest/mtkit
if [ $? -ne 0 ]; then exit 1; fi
elif [ -d /opt/minebest/mtkit ]; then elif [ -d /opt/minebest/mtkit ]; then
rm -rf /opt/minebest/mtkit rm -rf /opt/minebest/mtkit
if [ $? -ne 0 ]; then exit 1; fi
fi fi
if [ -L /tmp/lmk ]; then if [ -L /tmp/lmk ]; then
rm -f /tmp/lmk rm -f /tmp/lmk
if [ $? -ne 0 ]; then exit 1; fi
elif [ -d /tmp/lmk ]; then elif [ -d /tmp/lmk ]; then
rm -rf /tmp/lmk rm -rf /tmp/lmk
if [ $? -ne 0 ]; then exit 1; fi
fi fi
unzip /opt/minebest/prod/linux-minetest-kit.zip -d /tmp/lmk KIT_ZIP=/opt/minebest/assemble/prod/linux-minetest-kit.zip
mv /tmp/lmk/linux-minetest-kit /opt/minebest/mtkit if [ ! -f $KIT_ZIP ]; then
GOOD_MTKIT_FLAG="/opt/minebest/mtkit/mtcompile-program.sh" echo "Error: no $KIT_ZIP"
if [ ! -f "$GOOD_MTKIT_FLAG" ]; then
echo "Error: extracting /opt/minebest/prod/linux-minetest-kit.zip didn't seem to work. There is no '$GOOD_MTKIT_FLAG'."
exit 1 exit 1
fi fi
# mtkit rm -rf /tmp/lmk && unzip $KIT_ZIP -d /tmp/lmk
# - is the INSTALLED copy, rm -rf /opt/minebest/mtkit && mv /tmp/lmk/linux-minetest-kit /opt/minebest/mtkit
# - maybe OLDER than linux-minetest-kit.zip! GOOD_MTKIT_FLAG="/opt/minebest/mtkit/mtcompile-program.pl"
if [ ! -f "$GOOD_MTKIT_FLAG" ]; then
# prod echo "[pull-lmk] Error: extracting $KIT_ZIP didn't seem to work. There is no '$GOOD_MTKIT_FLAG'."
# - delete prod/* to test building prod using "bash -e buildskipwin.sh" exit 1
fi
# ^ exclude remotely-built copies of the program: # ^ exclude remotely-built copies of the program:
# - mtkit/minetest/ # - mtkit/minetest/
@ -47,25 +119,47 @@ fi
# --exclude "assemble/" \ # --exclude "assemble/" \
# --info=progress2 \ # --info=progress2 \
# #
if [ $code -eq 0 ]; then
echo "Updating /opt/minebest...OK" if [ ! -L /opt/minebest/mtkit/toolstree ]; then
else if [ -d /opt/minebest/mtkit/toolstree ]; then
echo "Updating /opt/minebest...FAILED" rm -Rf /opt/minebest/mtkit/toolstree
fi
fi
if [ ! -L /opt/minebest/mtkit/minetest ]; then
if [ -d /opt/minebest/mtkit/minetest ]; then
rm -Rf /opt/minebest/mtkit/minetest
fi
fi fi
cd /opt/minebest cd /opt/minebest
cat <<END cat <<END
[pull-lmk]
Next do (Linux instructions):
Next do: cd EnlivenMinetest && sudo ./install-minetest-build-deps.sh
# Observe any errors above and correct them if your operating system differs.
cd /opt/minebest/mtkit cd /opt/minebest/mtkit
# See /opt/minebest/mtkit/quickstart.txt for further guidance.
# Add stub (empty file) for deprecated library used by irrlicht-1.8.4/source/Irrlicht/COSOperator.cpp (to avoid missing sys/sysctl.h error compiling libraries)
sudo touch /usr/include/sys/sysctl.h
bash -e mtcompile-libraries.sh build bash -e mtcompile-libraries.sh build
perl mtcompile-program.pl --build --finetest --client perl mtcompile-program.pl build --finetest --client
# cd /opt/minebest/mtkit/minetest/bin # cd /opt/minebest/mtkit/minetest/bin
# nopackage install finetest --version rsync --caption "Finetest (rsync)" # nopackage install finetest --version rsync --caption "Finetest (rsync)"
# install-lmk # install-lmk
# ^ located in EnlivenMinetest/utilities # ^ located in EnlivenMinetest/utilities
# or upgrade but don't mess with worlds (other than builtin ones), nor mods etc: # or upgrade but don't mess with worlds (other than builtin ones), nor mods etc:
rsync -rt --exclude 'mods' --exclude 'minetest.conf' /opt/minebest/mtkit/minetest/ ~/finetest-rsync rsync -rtL --exclude 'mods' --exclude 'minetest.conf' /opt/minebest/mtkit/minetest/ ~/finetest-rsync
# or rsync -rtL --exclude 'mods' --exclude 'minetest.conf' /opt/minebest/mtkit/minetest/ ~/minetest-rsync
# or rsync -rtL --exclude 'mods' --exclude 'minetest.conf' /opt/minebest/mtkit/minetest/ ~/trolltest-rsync
# AND: # AND:
rsync -rt --delete /opt/minebest/mtkit/minetest/games/bucket_game/ ~/finetest-rsync/games/bucket_game rsync -rt --delete /opt/minebest/mtkit/minetest/games/bucket_game/ ~/finetest-rsync/games/bucket_game
# or rsync -rt --delete /opt/minebest/mtkit/minetest/games/bucket_game/ ~/minetest-rsync/games/bucket_game
# or rsync -rt --delete /opt/minebest/mtkit/minetest/games/bucket_game/ ~/trolltest-rsync/games/bucket_game
# debug:
valgrind -q --vgdb-error=0 --leak-check=full bin/minetest
# ^ then on crash, follow instructions provided in error to connect gdb to it.
# See https://developers.redhat.com/articles/2021/11/01/debug-memory-errors-valgrind-and-gdb#an_example_program_with_memory_errors
END END

10
utilities/run-any

@ -14,7 +14,6 @@ import subprocess
import copy import copy
import shlex import shlex
import platform import platform
from pprint import pformat
from collections import OrderedDict from collections import OrderedDict
SCRIPTS_DIR = os.path.dirname(os.path.realpath(__file__)) SCRIPTS_DIR = os.path.dirname(os.path.realpath(__file__))
@ -57,6 +56,7 @@ except ModuleNotFoundError as ex:
" for clickable Lua traceback lines" " for clickable Lua traceback lines"
% REPOS_DIR, file=sys.stderr) % REPOS_DIR, file=sys.stderr)
def echo0(*args): def echo0(*args):
print(*args, file=sys.stderr) print(*args, file=sys.stderr)
return True return True
@ -99,7 +99,7 @@ def show_and_return(cmd, enable_collect=False, cwd=None, shell=False):
# <https://cyberciti.biz/faq/python-run-external-command-and-get-output/>) # <https://cyberciti.biz/faq/python-run-external-command-and-get-output/>)
if shell not in [None, True, False]: if shell not in [None, True, False]:
raise ValueError("Expected None, True, or False for shell but got %s" raise ValueError("Expected None, True, or False for shell but got %s"
% pformat(shell)) % repr(shell))
if cwd is None: if cwd is None:
cwd = os.getcwd() cwd = os.getcwd()
if shell and not hasattr(cmd, "split"): if shell and not hasattr(cmd, "split"):
@ -139,7 +139,7 @@ def show_and_return(cmd, enable_collect=False, cwd=None, shell=False):
cmd = ["bash", "-c", "cd '" + cwd + "'; " + pre_bin + shlex.join(cmd)] cmd = ["bash", "-c", "cd '" + cwd + "'; " + pre_bin + shlex.join(cmd)]
else: else:
cmd = ["bash", "-c", "cd '" + cwd + "'; " + pre_bin + cmd] cmd = ["bash", "-c", "cd '" + cwd + "'; " + pre_bin + cmd]
run_msg = prefix+"Running %s" % pformat(cmd) run_msg = prefix+"Running %s" % repr(cmd)
run_msg += ' # shell=%s in "%s"' % (shell, cwd) run_msg += ' # shell=%s in "%s"' % (shell, cwd)
echo0(run_msg) echo0(run_msg)
if cwd is not None: if cwd is not None:
@ -297,7 +297,7 @@ def show_and_return(cmd, enable_collect=False, cwd=None, shell=False):
if len(meta['string']) > 0: if len(meta['string']) > 0:
raise NotImplementedError( raise NotImplementedError(
"The remaining %s wasn't processed: %s" "The remaining %s wasn't processed: %s"
% (_, pformat(meta['string'])) % (_, repr(meta['string']))
) )
return { return {
'code': code, 'code': code,
@ -386,7 +386,7 @@ def main():
if count < 1: if count < 1:
cmd_msg = cmd if isinstance(cmd, str) else shlex.join(cmd) cmd_msg = cmd if isinstance(cmd, str) else shlex.join(cmd)
print(" (Output of `%s` had %s line(s) of output.)" print(" (Output of `%s` had %s line(s) of output.)"
% (pformat(cmd_msg), count), % (repr(cmd_msg), count),
file=sys.stderr) file=sys.stderr)
else: else:
print("", file=sys.stderr) print("", file=sys.stderr)

Loading…
Cancel
Save