|
|
@ -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.") |
|
|
|
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 |
|
|
|
# TODO: maybe detect it: |
|
|
|
# else: |
|
|
|
# echo0("using detected project: {}".format(project_info)) |
|
|
|
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] |
|
|
|
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, |
|
|
|
pformat(project_meta.get('shortcut_exe_relpaths')) |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
results = install_minetest( |
|
|
|
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_meta(mt_share_path): |
|
|
|
"""Detect the project info from a source *or* destination. |
|
|
|
|
|
|
|
|
|
|
|
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. |
|
|
|
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) |
|
|
|
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: |
|
|
|
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 |
|
|
|
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={}): |
|
|
|