#!/usr/bin/env python3
# encoding: utf-8
"""
wmlunits -- tool to output information on all units in HTML
Run without arguments to see usage.
"""
import argparse
import os
import shutil
import sys
import traceback
import subprocess
import multiprocessing
import queue
import yaml
import wesnoth.wmlparser3 as wmlparser3
import unit_tree.helpers as helpers
import unit_tree.animations as animations
import unit_tree.html_output as html_output
import unit_tree.overview
import unit_tree.wiki_output as wiki_output
TIMEOUT = 20
def copy_images():
print("Recolorizing pictures.")
image_collector.copy_and_color_images(options.output)
shutil.copy2(os.path.join(os.path.dirname(os.path.realpath(__file__)),
"unit_tree/menu.js"),
options.output)
def shell(com):
#print(com)
p = subprocess.Popen(com,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True)
out, err = p.communicate()
#if out: sys.stdout.write(out)
#if err: sys.stdout.write(err)
return p.returncode
def shell_out(com):
p = subprocess.Popen(com,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = p.communicate()
return out
def bash(name):
return "'" + name.replace("'", "'\\''") + "'"
def move(f, t, name):
if os.path.exists(f + "/" + name + ".cfg"):
com = "mv " + f + "/" + bash(name + ".cfg") + " " + t + "/"
shell(com)
com = "mv " + f + "/" + bash(name) + " " + t + "/"
return shell(com)
_info = {}
def get_info(addon):
global _info
if addon in _info:
return _info[addon]
_info[addon] = None
try:
path = options.addons + "/" + addon + "/_info.cfg"
if os.path.exists(path):
parser = wmlparser3.Parser(options.wesnoth,
options.config_dir,
options.data_dir)
parser.parse_file(path)
_info[addon] = parser
else:
print("Cannot find " + path)
except wmlparser3.WMLError as e:
print(e)
return _info[addon]
_deps = {}
global_addons = set()
def get_dependencies(addon):
global _deps
global global_addons
if addon in _deps:
return _deps[addon]
_deps[addon] = []
try:
info = get_info(addon).get_all(tag="info")[0]
row = info.get_text_val("dependencies")
if row:
deps1 = row.split(",")
else:
deps1 = []
for d in deps1:
if d in global_addons:
_deps[addon].append(d)
else:
print("Missing dependency for " + addon + ": " + d)
except Exception as e:
print(e)
return _deps[addon]
def set_dependencies(addon, depends_on):
_deps[addon] = depends_on
def get_all_dependencies(addon):
result = []
check = get_dependencies(addon)[:]
while check:
d = check.pop()
if d == addon or d in result:
continue
result.append(d)
check += get_dependencies(d)
return result
def sorted_by_dependencies(addons):
sorted = []
unsorted = addons[:]
while unsorted:
n = 0
for addon in unsorted:
for d in get_dependencies(addon):
if d not in sorted:
break
else:
sorted.append(addon)
unsorted.remove(addon)
n += 1
continue
if n == 0:
print("Cannot sort dependencies for these addons: " + str(unsorted))
sorted += unsorted
break
return sorted
def search(batchlist, name):
for info in batchlist:
if info and info["name"] == name:
return info
batchlist.append({})
batchlist[-1]["name"] = name
return batchlist[-1]
def list_contents():
class Empty:
pass
local = Empty()
mainline_eras = set()
filename = options.list
def append(info, id, define, c=None, name=None, domain=None):
info.append({})
info[-1]["id"] = id
info[-1]["define"] = define
info[-1]["name"] = c.get_text_val("name") if c else name
info[-1]["units"] = "?"
info[-1]["translations"] = {}
for isocode in languages:
translation = html_output.Translation(options.transdir, isocode)
def translate(string, domain):
return translation.translate(string, domain)
if c:
t = c.get_text_val("name", translation=translate)
else:
t = translate(name, domain)
if t != info[-1]["name"]:
info[-1]["translations"][isocode] = t
def get_dependency_eras(batchlist, addon):
dependency_eras = list(mainline_eras)
for d in get_all_dependencies(addon):
dinfo = search(batchlist, d)
for era in dinfo.get("eras", []):
dependency_eras.append(era["id"])
return dependency_eras
def list_eras(batchlist, addon):
eras = local.wesnoth.parser.get_all(tag="era")
if addon != "mainline":
dependency_eras = get_dependency_eras(batchlist, addon)
eras = [x for x in eras if not x.get_text_val("id") in dependency_eras]
info = []
for era in eras:
eid = era.get_text_val("id")
if addon == "mainline":
mainline_eras.add(eid)
append(info, eid, "MULTIPLAYER", c=era)
return info
def list_campaigns(batchlist, addon):
campaigns = local.wesnoth.parser.get_all(tag="campaign")
info = []
for campaign in campaigns:
cid = campaign.get_text_val("id")
d = campaign.get_text_val("define")
d2 = campaign.get_text_val("extra_defines")
if d2:
d += "," + d2
d3 = campaign.get_text_val("difficulties")
if d3:
d += "," + d3.split(",")[0]
else:
difficulty = campaign.get_all(tag="difficulty")
if difficulty:
d3 = difficulty[0].get_text_val("define")
if d3:
d += "," + d3
append(info, cid, d, c=campaign)
return info
def parse(wml, defines):
def f(options, wml, defines, q):
local.wesnoth = helpers.WesnothList(
options.wesnoth,
options.config_dir,
options.data_dir,
options.transdir)
#print("remote", local.wesnoth)
try:
local.wesnoth.parser.parse_text(wml, defines)
q.put(("ok", local.wesnoth))
except Exception as e:
q.put(("e", e))
q = multiprocessing.Queue()
p = multiprocessing.Process(target=f, args=(options, wml, defines, q))
p.start()
try:
s, local.wesnoth = q.get(timeout=TIMEOUT)
except queue.Empty:
p.terminate()
raise
#print("local", s, local.wesnoth)
p.join()
if s == "e":
remote_exception = local.wesnoth
raise remote_exception
def get_version(addon):
parser = get_info(addon)
if parser:
for info in parser.get_all(tag="info"):
return info.get_text_val("version") + "*" + info.get_text_val("uploads")
try:
os.makedirs(options.output + "/mainline")
except OSError:
pass
try:
batchlist = yaml.load(open(options.list))
except IOError:
batchlist = []
print("mainline")
info = search(batchlist, "mainline")
info["version"] = "mainline"
info["parsed"] = "false"
parse("{core}{multiplayer/eras.cfg}", "__WMLUNITS__,SKIP_CORE")
info["eras"] = list_eras(batchlist, "mainline")
# Fake mainline campaign to have an overview of the mainline units
info["campaigns"] = []
append(info["campaigns"], "mainline", "", name="Units", domain="wesnoth-help")
if not options.addons_only:
parse("{core}{campaigns}", "__WMLUNITS__,SKIP_CORE")
info["campaigns"] += list_campaigns(batchlist, "mainline")
addons = []
if options.addons:
addons = os.listdir(options.addons)
global global_addons
global_addons = set(addons)
# fill in the map for all dependencies
for addon in addons:
get_dependencies(addon)
# this ensures that info about eras in dependant addons is available
# already
addons = sorted_by_dependencies(addons)
for i, addon in enumerate(addons):
if not os.path.isdir(options.addons + "/" + addon):
continue
mem = "? KB"
try:
mem = dict([x.split("\t", 1) for x in open("/proc/self/status").read().split("\n") if x])["VmRSS:"]
except:
pass
sys.stdout.write("%4d/%4d " % (1 + i, len(addons)) + addon + " [" + mem + "] ... ")
sys.stdout.flush()
d = options.output + "/" + addon
logname = d + "/error.log"
try:
os.makedirs(d)
except OSError:
pass
version = get_version(addon)
move(options.addons, options.config_dir + "/data/add-ons", addon)
for d in get_dependencies(addon):
move(options.addons, options.config_dir + "/data/add-ons", d)
try:
info = search(batchlist, addon)
if info.get("version", "") == version and info.get("parsed", False) is True:
sys.stdout.write("up to date\n")
continue
info["parsed"] = False
info["dependencies"] = get_dependencies(addon)
parse("{core}{multiplayer}{multiplayer/eras.cfg}{~add-ons}", "__WMLUNITS__,MULTIPLAYER,SKIP_CORE")
info["eras"] = list_eras(batchlist, addon)
info["campaigns"] = list_campaigns(batchlist, addon)
info["version"] = version
sys.stdout.write("ok\n")
except wmlparser3.WMLError as e:
ef = open(logname, "w")
ef.write("
%s
\n" % (u.rid,)) f.write("