Difference between revisions of "Maintenance tools"

From The Battle for Wesnoth Wiki
(wmllint)
m (Corrected small typo.)
(23 intermediate revisions by 15 users not shown)
Line 1: Line 1:
 +
<div class="floatright"> __TOC__ </div>
 +
 
The Wesnoth source code distribution includes a couple of tools intended to help authors maintain campaigns, faction & unit packs, and other WML resources. These
 
The Wesnoth source code distribution includes a couple of tools intended to help authors maintain campaigns, faction & unit packs, and other WML resources. These
 
are:
 
are:
Line 8: Line 10:
 
; wmlindent: a utility for reindenting WML to a uniform style.
 
; wmlindent: a utility for reindenting WML to a uniform style.
  
You will need a Python interpreter on your system to use these tools.  Linux, *BSD, and Mac OS/X should already have Python installed; for Windows it's a free download
+
; GUI.pyw: a graphical interface
from http://www.python.org.  You will also need to know how to run command-line tools
+
 
on your system.
+
== General Information ==
 +
 
 +
You will need a Python 3 interpreter on your system to use these tools.  Linux, *BSD, and Mac OS/X should already have Python 3 installed; for Windows it's a free download
 +
from http://www.python.org.  You will also need to know how to run command-line tools on your system.
 +
 
 +
If you're working with Debian or Ubuntu you might have to install the package wesnoth-1.14-tools (or the convenient version).
 +
sudo apt install wesnoth-1.14-tools
 +
 
  
 
All three tools will require you to supply a <i>directory list</i>.  This is a set of directories containing the WML files you want to work on.
 
All three tools will require you to supply a <i>directory list</i>.  This is a set of directories containing the WML files you want to work on.
 +
 +
This page is intended as documentation for users.
  
 
<u>Note to Windows Users:</u> This means you have to run it from the '''Command Line'''. The command line may be reached by hitting Start, then Run, then "cmd" or "command" depending on your version of Windows.
 
<u>Note to Windows Users:</u> This means you have to run it from the '''Command Line'''. The command line may be reached by hitting Start, then Run, then "cmd" or "command" depending on your version of Windows.
  
 
Example uses:
 
Example uses:
  python wmllint --oldversion=1.3.4 path\to\files
+
  python wmllint path\to\files
 
  python wmlindent path\to\files
 
  python wmlindent path\to\files
  
 
Another example:
 
Another example:
  "C:\Program Files\Python2.4\python.exe" data\tools\wmllint --dryrun data\core data\{multiplayer,themes} data\campaigns  
+
  "C:\Program Files\Python3.7\python.exe" data\tools\wmllint --dryrun data\core data\{multiplayer,themes} data\campaigns  
 
(You have to specify the full directory path to the executable if you don't have your environment variables set up correctly).
 
(You have to specify the full directory path to the executable if you don't have your environment variables set up correctly).
 
The first thing you type is the path to your python executable, followed by a space. The second thing you type is the path to the desired script to run, followed by a space. The third thing you type is the path to the folder (or file) to be processed.
 
The first thing you type is the path to your python executable, followed by a space. The second thing you type is the path to the desired script to run, followed by a space. The third thing you type is the path to the folder (or file) to be processed.
 +
 +
 +
'''A convenient way of running wmllint''' on Linux (Debian or Ubuntu) and Windows in comparison, '''Linux''':
 +
 +
Assuming we're working with wesnoth 1.14 or more advanced versions.
 +
python3 /usr/share/games/wesnoth/1.14/data/tools/wmllint --dryrun /usr/share/games/wesnoth/1.14/data/core ~/.local/share/wesnoth/1.14/data/add-ons/A_Simple_Campaign 1>wmllint-run.log 2>wmllint-err.log
 +
I have these commands inside of a file named
 +
wmllint_dryrun_ASC.sh
 +
and execute it by opening a shell (=terminal, console, command window, bash,...), navigating into the directory with that file and typing
 +
bash wmllint_dryrun_ASC.sh
 +
The python3 command should be automatically known on Debian. The path to the script tells the python interpreter what to execute. --dryrun: A wmllint option, see below. The path to the core files is needed to let wmllint know about e.g. defined core units, followed by the path to the add-on that shall be checked; the last two commands cause the result of the wmllint usage to be written into those files in the same directory as the script.
 +
'''Windows''', this is logically exactly the same as the Linux shell script above, so if you are on a Mac you can probably conclude how you need to adapt the paths:
 +
E:\Python37\python.exe E:\Programme\Wesnoth_1.14_git\data\tools\wmllint --dryrun E:\Programme\Wesnoth_1.14_git\data\core E:\Programme\Wesnoth_1.14_git\userdata\data\add-ons\A_Simple_Campaign 1>wmllint-run.log 2>wmllint-err.log
 +
This is the content of a .txt file, whose extension I rename to .bat and double-click onto it. Opening a command window is not needed this way.
 +
Since Python isn't natively installed on windows and I don't have environment variables set, the full path to python.exe is given. If your directories contain spaces it may help to include the path in quotes:
 +
"C:\Programs\Battle for Wesnoth 1.14\data\tools\wmllint"
 +
Remember that you do not need to enter all of the commands/paths at once. If it doesn't work, start with only "python" or "C:\Python37\python.exe" or the like and interpret the error messages that you get. If you get an "unknown command", python isn't installed or environment variables aren't set correctly. After that, you can add the later commands one by one.
  
 
== wmlscope ==
 
== wmlscope ==
  
The main use for <tt>wmlscope</tt> is to find WML macro references without definitions and references to resource files (sounds and images) that don't exist.  These are difficult to spot from in-game because they usually result in silence or a missing image rather than actual broken game logic.  They may happen because of typos in your WML, or because the name of a macro or the location of a resource file changed betweeen versions of the game.
+
The main use for <tt>wmlscope</tt> is to find WML macro references without definitions and references to resource files (sounds and images) that don't exist.  These are difficult to spot from in-game because they usually result in silence or a missing image rather than actual broken game logic.  They may happen because of typos in your WML, or because the name of a macro or the location of a resource file changed between versions of the game.
  
 
<tt>wmlscope</tt> also checks macro invocations for consistency.  It will complain
 
<tt>wmlscope</tt> also checks macro invocations for consistency.  It will complain
 
if a macro is called with the wrong number of arguments.  In most cases it can deduce information about the type of the literal expected to be passed to a given macro argument by looking at the name of the formal.
 
if a macro is called with the wrong number of arguments.  In most cases it can deduce information about the type of the literal expected to be passed to a given macro argument by looking at the name of the formal.
  
<table border="1"><tr>
+
<table class="wikitable"><tr>
 
<th>Type</th>
 
<th>Type</th>
<th>Meanining</th>
+
<th>Meaning</th>
 
<th>Formals requiring this type</th>
 
<th>Formals requiring this type</th>
 
<th>Literals of this type</th>
 
<th>Literals of this type</th>
 
</tr>
 
</tr>
 
<tr>
 
<tr>
<td>macro</td>
+
<td>side</td>
<td>A macro invocation</td>
+
<td>a single side number</td>
<td></td>
+
<td>SIDE, *_SIDE, SIDE[0-9]</td>
<td>{.*}</td>
+
<td>a numeric or "global"</td>
 
</tr>
 
</tr>
 
<tr>
 
<tr>
 
<td>numeric</td>
 
<td>numeric</td>
<td>a numeric literal</td>
+
<td>a numeric integer literal</td>
<td>SIDE, X, Y, RED, GREEN, BLUE, TURN, RADIUS, *NUMBER, *AMOUNT, *_X, *_Y</td>
+
<td>SIDE, X, Y, RED, GREEN, BLUE, TURN, PROB, LAYER, TIME, *_SIDE, *NUMBER, *AMOUNT, *COST, *RADIUS, *_X, *_Y, *_INCREMENT, *_FACTOR, *_TIME, *_SIZE, DURATION</td>
 
<td>\-?[0-9]+</td>
 
<td>\-?[0-9]+</td>
 +
</tr>
 +
<tr>
 +
<td>percentage</td>
 +
<td>a percentage</td>
 +
<td>*PERCENTAGE</td>
 +
<td>a numeric or 0\.[0-9]+</td>
 
</tr>
 
</tr>
 
<tr>
 
<tr>
 
<td>position</td>
 
<td>position</td>
<td>a coordinate, coordinate range, or set of coordinate ranges</td>
+
<td>a single x,y coordinate</td>
<td>POSITION</td>
+
<td>POSITION, *_POSITION, BASE</td>
<td>([0-9]+\-[0-9]+,?|[0-9]+,?)+</td>
+
<td>-?[0-9]+,-?[0-9]+</td>
 
</tr>
 
</tr>
 
<tr>
 
<tr>
 
<td>span</td>
 
<td>span</td>
<td>a coordinate, coordinate range, or set of coordinate ranges</td>
+
<td>a set of coordinates or coordinate ranges</td>
 
<td>*_SPAN</td>
 
<td>*_SPAN</td>
<td>"melee" or "ranged"</td>
+
<td>a numeric, position or ([0-9]+\-[0-9]+,?|[0-9]+,?)+</td>
 +
</tr>
 +
<tr>
 +
<td>alliance</td>
 +
<td>a set of side numbers</td>
 +
<td>SIDES, *_SIDES</td>
 +
<td>a span, or the empty string</td>
 
</tr>
 
</tr>
 
<tr>
 
<tr>
Line 72: Line 112:
 
<td>an alignment keyword</td>
 
<td>an alignment keyword</td>
 
<td>ALIGN</td>
 
<td>ALIGN</td>
<td>"lawful" or "neutral" or "chaotic" {{DevFeature}}</td>
+
<td>"lawful" or "neutral" or "chaotic"</td>
</tr><tr>
+
</tr>
<td>image</td>
+
<tr>
<td>an image name</td>
+
<td>types</td>
<td>IMAGE</td>
+
<td>a set of unit types</td>
<td>string ending in .png or .jpg</td>
+
<td>TYPES</td>
 +
<td>a shortname, name, or anything that contains spaces and matches no other type</td>
 +
</tr>
 +
<tr>
 +
<td>terrain_pattern</td>
 +
<td>a set of terrain codes to filter</td>
 +
<td>ADJACENT*, TERRAINLIST*, *TERRAIN_PATTERN, RESTRICTING</td>
 +
<td>a terrain_code or name</td>
 +
</tr>
 +
<tr>
 +
<td>terrain_code</td>
 +
<td>a single terrain code, perhaps with overlay</td>
 +
<td>TERRAIN*, *TERRAIN</td>
 +
<td>a shortname or (\*|[A-Z][a-z]+)\^([A-Z][a-z\\|/]+\Z)?</td>
 +
</tr>
 +
<tr>
 +
<td>shortname</td>
 +
<td>a terrain code or a short, capitalized variable name</td>
 +
<td></td>
 +
<td>[A-Z][a-z][a-z]?</td>
 +
<tr>
 +
<td>name</td>
 +
<td>a name or identifier</td>
 +
<td>NAME, VAR, IMAGESTEM, ID, FLAG, *_NAME, *_ID, NAMESPACE, BUILDER, *_VAR</td>
 +
<td>anything without spaces that matches no other type</td>
 +
</tr>
 +
<tr>
 +
<td>optional_string</td>
 +
<td>a string value (may be empty)</td>
 +
<td>ID_STRING, NAME_STRING, DESCRIPTION, IPF</td>
 +
<td>a string, or the empty string</td>
 +
</tr>
 +
<tr>
 +
<td>string</td>
 +
<td>a nonempty string not matching any of the preceding types</td>
 +
<td>STRING, TYPE, TEXT, *_STRING, *_TYPE, *_TEXT</td>
 +
<td>a shortname, a name, a stringliteral, or anything that contains spaces and matches no other type</td>
 +
</tr>
 +
<tr>
 +
<td>stringliteral</td>
 +
<td>a string in doublequotes or a translated string</td>
 +
<td></td>
 +
<td>".*" or _.* but not _[a-z].*</td>
 
</tr>
 
</tr>
 
<tr>
 
<tr>
<td>terrain</td>
+
<td>image</td>
<td>a terrain code</td>
+
<td>an image path, perhaps with [[ImagePathFunctionWML|image path functions]]</td>
<td>TERRAIN</td>
+
<td>*IMAGE, PROFILE</td>
<td>[A-Z][a-z]+\^[A-Z][a-z\\|/]+\Z</td>
+
<td>[A-Za-z0-9{}.][A-Za-z0-9_/+{}.-]*\.(png|jpg)(?=(~.*)?)</td>
 
</tr>
 
</tr>
 
<tr>
 
<tr>
 
<td>sound</td>
 
<td>sound</td>
<td>a music or sound resource</td>
+
<td>a music or sound filename</td>
 
<td>MUSIC, SOUND</td>
 
<td>MUSIC, SOUND</td>
<td>\.wav|\.ogg</td>
+
<td>string ending with ".wav" or ".ogg"</td>
 
</tr>
 
</tr>
 
<tr>
 
<tr>
 
<td>filter</td>
 
<td>filter</td>
<td>filter expression</td>
+
<td>[[FilterWML|WML filter]]</td>
 
<td>FILTER</td>
 
<td>FILTER</td>
 
<td>any non-quoted string containing "="</td>
 
<td>any non-quoted string containing "="</td>
Line 99: Line 181:
 
<tr>
 
<tr>
 
<td>WML</td>
 
<td>WML</td>
<td>a WML fragment</td>
+
<td>arbitrary WML fragment</td>
<td>*_WML</td>
+
<td>WML, *_WML</td>
 
<td>any non-quoted string containing "=", or the empty string</td>
 
<td>any non-quoted string containing "=", or the empty string</td>
 
</tr>
 
</tr>
 
<tr>
 
<tr>
<td>name</td>
+
<td>affix</td>
<td>a name or ID</td>
+
<td>a prefix, suffix, or infix for a variable name</td>
<td>NAME, VAR, IMAGESTEM, ID</td>
+
<td>AFFIX, *AFFIX, POSTFIX, ROTATION</td>
<td>[A-Z][a-z]+\^[A-Z][a-z\\|/]+\Z</td>
+
<td>a shortname or name, or the empty string</td>
</tr>
 
<tr>
 
<td>string</td>
 
<td>a nonempty string not matching any of the preceding types</td>
 
<td>TYPE, TEXT</td>
 
<td>Anything surrounded by doublequotes,  
 
'''or'' anything with embedded spaces that doesn't match one of the preceding types</td>
 
 
</tr>
 
</tr>
 
<tr>
 
<tr>
<td>optional string</td>
+
<td>any</td>
<td>a string value (may be empty)</td>
 
<td>DESCRIPTION, USER_DESCRIPTION</td>
 
<td>Anything surrounded by doublequotes,
 
'''or'' anything with embedded spaces that doesn't match one of the preceding types</td>
 
</tr>
 
<tr>
 
<td>value</td>
 
 
<td>anything</td>
 
<td>anything</td>
<td>*VALUE</td>
+
<td>*VALUE, [ARS][0-9]</td>
 
<td>anything</td>
 
<td>anything</td>
 
</tr>
 
</tr>
 
</table>
 
</table>
  
Any numeric or position is accepted by a span argument.  Any image or name is accepted for a string argument. Any actual argument that is a macro is accepted for any formal. Otherwise, if the formal has an identifiable type, <tt>wmlscope</tt> will complain if the actual literal does not match it.
+
If the actual argument is a macro call {.*}, then it matches any formal  Otherwise, if the formal has an identifiable type, <tt>wmlscope</tt> will complain if the actual literal does not match it.
  
 
The argument type check only works in macro calls that fit on a single line.
 
The argument type check only works in macro calls that fit on a single line.
Line 165: Line 233:
 
* Resource files and macro names may change between versions of the game. <tt>wmllint</tt> knows about these changes and will tweak your WML to fit where it can.
 
* Resource files and macro names may change between versions of the game. <tt>wmllint</tt> knows about these changes and will tweak your WML to fit where it can.
  
* Between 1.2.x and 1.3.1 the terrain-coding system used in map files underwent a major change. It changed again in a minor way between 1.3.1 and 1.3.2. <tt>wmllint</tt> will translate your maps for you, unless you use custom terrains in which case you will have to do it by hand.
+
* Between 1.2.x and 1.3.1 the terrain-coding system used in map files underwent a major change. It changed again in a minor way between 1.3.1 and 1.3.2. If you port such old code, use <tt>wmllint-1.4</tt>, which is located in the same directory as <tt>wmllint</tt>. It will translate your maps for you, unless you use custom terrains in which case you will have to do it by hand.
  
 
<tt>wmllint</tt> also performs various sanity-checking operations, reporting:
 
<tt>wmllint</tt> also performs various sanity-checking operations, reporting:
  
 
* unbalanced tags
 
* unbalanced tags
* strings that need a translation mark and should not have them
+
* strings that need a translation mark and do not have them
 
* strings that have a translation mark and should not
 
* strings that have a translation mark and should not
 
* translatable strings containing macro references  
 
* translatable strings containing macro references  
Line 185: Line 253:
 
; -v, --verbose:              Set verbosity; more details below.
 
; -v, --verbose:              Set verbosity; more details below.
 
; -c, --clean:                Clean up -bak files.
 
; -c, --clean:                Clean up -bak files.
; -D, --diff:                Show diffs between unconverted and unconverted files.
+
; -D, --diff:                Show diffs between converted and unconverted files.
 
; -r, --revert:              Revert the conversion from the -bak files.
 
; -r, --revert:              Revert the conversion from the -bak files.
 
; -n, --nolift:              Suppress lifting, do sanity checks only  
 
; -n, --nolift:              Suppress lifting, do sanity checks only  
Line 205: Line 273:
 
# Use either --clean to remove the -bak files or --revert to undo the conversion.
 
# Use either --clean to remove the -bak files or --revert to undo the conversion.
  
In the development branch, wmllint has the additional feature that it tries to locate a spell checker on your system and spell-checks storyline and message strings.  It will work automatically with either aspell, myspell, or ispell provided you have the <tt>enchant.py</tt> Python library installed.
+
Additionally, wmllint tries to locate a spell checker on your system and spell-checks storyline and message strings.  It will work automatically with either aspell, myspell, or ispell provided you have the <tt>enchant.py</tt> Python library installed.
 +
 
 +
wmllint supports a number of magic comments to customize its behaviour and avoid false positives. All magic wmllint comments begin with the string <tt>wmllint:</tt>, followed by some additional keyword and potentially some arguments. In the below explanations, a string of the form <code>[a|b]</code> means you may use either <code>a</code> or <code>b</code> at that location, while a string of the form <code><arg></code> indicates free text substitution.
 +
 
 +
* <code>ignore</code>: Disables checking of terrains and translation marks on the current line only.
 +
* <code>noconvert</code>: Disables conversion of terrains and image/sound filenames on the current line only.
 +
* <code>markcheck [on|off]</code>: Enables or disables translation mark checking from the current (next?) line onward.
 +
* <code>no-icon</code>: Prevents description insertions (what does that mean?)
 +
* <code>recognize <name></code>: Indicates that the character with the given name exists even though a declaration (ie a <code>[unit]</code> or <code>[side]</code> tag) is not visible.
 +
* <code>whofield <macro> <number></code>: Indicates that the specified macro declares a character whose name is given by the specified argument to the macro.
 +
* <code>whofield clear <macro></code>: Removes the <code>whofield</code> definition for the specified macro.
 +
* <code>who <macro> is <name> <name> ...</code>: Indicates that the specified macro declares one or more characters whose names are given as a space-separated list. This can also be used for macros that auto-recall a list of characters, some of which join later or may die at some point. If a definition for the macro already exists, then the specified names are appended to it. If a name is preceded by a double minus (<code>-- <name></code>) then it is removed from the definition.
 +
* <code>unwho all|<name></code>: Removes the <code>who</code> definition for the specified macro, or all <code>who</code> definitions.
 +
* <code>usage of "<unit>" is <class></code>: Declares the usage of the specified unit (the <code>usage=</code> key in the <code>[unit_type]</code> tag). Useful if you are using macros to generate several similar unit types.
 +
* <code>usagetype <class></code>: Declares a valid usage type for units. <code>usagetypes</code> is also recognized, and a comma-separated list of usage types can be specified.
 +
* <code>validate-[on|off]</code>: Enables or disables stack-based validation checks. Use when you have unbalanced tags in macros.
 +
* <code>unbalanced-[on|off]</code>: Similar to above, the precise difference is unclear.
 +
* <code>no translatables</code>: Suppresses warnings about a missing textdomain declaration in the current file. Make sure the file really does have no translatable strings!
 +
* <code>display [on|off]</code>: Enable or disable warnings about newlines in messages.
 +
* <code>notecheck [on|off]</code>: Enable or disable note consistency checks for unit descriptions.
 +
* <code>deathcheck [on|off]</code>: Enable or disable the check for units speaking in their death events.
 +
* <code>[general|directory|local] spellings <word> <word> ...</code>: Declares the specified space-separated list of words to be valid spellings in the specified context. The context <code>local</code> indicates the current file only, while <code>directory</code> means the current file and any siblings in the same directory or subdirectories. The <code>global</code> context indicates the spellings are valid anywhere.
 +
* <code>no spellcheck</code>: Disables spell checking on the current line.
 +
* <code>skip-side</code>: Indicates that there is a missing side declaration at this location that will be provided by a macro expansion.
 +
* <code>match <string> with <notes_macro></code>: Indicates that uses of the specified string in a unit definition (usually a macro, including the curly braces) should be matched up with the specified special notes macro (which is also specified as the full macro with curly braces).
 +
* <code>no ellipsecheck</code>: Disables checking of unit ellipses on the current line.
  
 
== wmlindent ==
 
== wmlindent ==
Line 225: Line 318:
 
that is syntactically unbalanced.  Unbalanced double quotes that aren't part
 
that is syntactically unbalanced.  Unbalanced double quotes that aren't part
 
of a multiline literal will also confuse it.  You will receive warnings
 
of a multiline literal will also confuse it.  You will receive warnings
oiif there's an indent open at end of file or if a closer occurs with
+
if there's an indent open at end of file or if a closer occurs with
 
indent already zero; these two conditions strongly suggest unbalanced WML.
 
indent already zero; these two conditions strongly suggest unbalanced WML.
 +
 +
== GUI.pyw ==
 +
 +
Starting from version 1.11.15 and 1.13.0, a GUI (written in Tkinter, plus the themed widgets ttk) is available in the same directory as the other tools. To use it, you need to have a version of Python equal to or greater than 3.1.0 (the 3.0.x series doesn't include the ttk widgets, and as such is unsuitable for this script).
 +
 +
If you're on Linux, be sure to have installed the ''python3-tk'' module, '''or the application won't run at all'''. To install it in a Debian-based distro (like Ubuntu), type this line in a Terminal:
 +
sudo apt install python3-tk
 +
 +
To start it, just double click on the GUI.pyw file. The interface is pretty much self-explanatory, and allows you to run wmllint, wmlscope, wmlindent and wmlxgettext, modify their options, select an add-on and save the tools' output as a text file.
  
 
[[Category:Create]]
 
[[Category:Create]]
 
[[Category:Tools]]
 
[[Category:Tools]]

Revision as of 09:39, 4 April 2020

The Wesnoth source code distribution includes a couple of tools intended to help authors maintain campaigns, faction & unit packs, and other WML resources. These are:

wmlscope
a cross-reference lister, useful for finding unresolved macro and resource-file references.
wmllint
a utility for sanity-checking WML syntax and porting your old WML to the current version of WML.
wmlindent
a utility for reindenting WML to a uniform style.
GUI.pyw
a graphical interface

General Information

You will need a Python 3 interpreter on your system to use these tools. Linux, *BSD, and Mac OS/X should already have Python 3 installed; for Windows it's a free download from http://www.python.org. You will also need to know how to run command-line tools on your system.

If you're working with Debian or Ubuntu you might have to install the package wesnoth-1.14-tools (or the convenient version).

sudo apt install wesnoth-1.14-tools


All three tools will require you to supply a directory list. This is a set of directories containing the WML files you want to work on.

This page is intended as documentation for users.

Note to Windows Users: This means you have to run it from the Command Line. The command line may be reached by hitting Start, then Run, then "cmd" or "command" depending on your version of Windows.

Example uses:

python wmllint path\to\files
python wmlindent path\to\files

Another example:

"C:\Program Files\Python3.7\python.exe" data\tools\wmllint --dryrun data\core data\{multiplayer,themes} data\campaigns 

(You have to specify the full directory path to the executable if you don't have your environment variables set up correctly). The first thing you type is the path to your python executable, followed by a space. The second thing you type is the path to the desired script to run, followed by a space. The third thing you type is the path to the folder (or file) to be processed.


A convenient way of running wmllint on Linux (Debian or Ubuntu) and Windows in comparison, Linux:

Assuming we're working with wesnoth 1.14 or more advanced versions.

python3 /usr/share/games/wesnoth/1.14/data/tools/wmllint --dryrun /usr/share/games/wesnoth/1.14/data/core ~/.local/share/wesnoth/1.14/data/add-ons/A_Simple_Campaign 1>wmllint-run.log 2>wmllint-err.log

I have these commands inside of a file named

wmllint_dryrun_ASC.sh

and execute it by opening a shell (=terminal, console, command window, bash,...), navigating into the directory with that file and typing

bash wmllint_dryrun_ASC.sh

The python3 command should be automatically known on Debian. The path to the script tells the python interpreter what to execute. --dryrun: A wmllint option, see below. The path to the core files is needed to let wmllint know about e.g. defined core units, followed by the path to the add-on that shall be checked; the last two commands cause the result of the wmllint usage to be written into those files in the same directory as the script. Windows, this is logically exactly the same as the Linux shell script above, so if you are on a Mac you can probably conclude how you need to adapt the paths:

E:\Python37\python.exe E:\Programme\Wesnoth_1.14_git\data\tools\wmllint --dryrun E:\Programme\Wesnoth_1.14_git\data\core E:\Programme\Wesnoth_1.14_git\userdata\data\add-ons\A_Simple_Campaign 1>wmllint-run.log 2>wmllint-err.log

This is the content of a .txt file, whose extension I rename to .bat and double-click onto it. Opening a command window is not needed this way. Since Python isn't natively installed on windows and I don't have environment variables set, the full path to python.exe is given. If your directories contain spaces it may help to include the path in quotes:

"C:\Programs\Battle for Wesnoth 1.14\data\tools\wmllint"

Remember that you do not need to enter all of the commands/paths at once. If it doesn't work, start with only "python" or "C:\Python37\python.exe" or the like and interpret the error messages that you get. If you get an "unknown command", python isn't installed or environment variables aren't set correctly. After that, you can add the later commands one by one.

wmlscope

The main use for wmlscope is to find WML macro references without definitions and references to resource files (sounds and images) that don't exist. These are difficult to spot from in-game because they usually result in silence or a missing image rather than actual broken game logic. They may happen because of typos in your WML, or because the name of a macro or the location of a resource file changed between versions of the game.

wmlscope also checks macro invocations for consistency. It will complain if a macro is called with the wrong number of arguments. In most cases it can deduce information about the type of the literal expected to be passed to a given macro argument by looking at the name of the formal.

Type Meaning Formals requiring this type Literals of this type
side a single side number SIDE, *_SIDE, SIDE[0-9] a numeric or "global"
numeric a numeric integer literal SIDE, X, Y, RED, GREEN, BLUE, TURN, PROB, LAYER, TIME, *_SIDE, *NUMBER, *AMOUNT, *COST, *RADIUS, *_X, *_Y, *_INCREMENT, *_FACTOR, *_TIME, *_SIZE, DURATION \-?[0-9]+
percentage a percentage *PERCENTAGE a numeric or 0\.[0-9]+
position a single x,y coordinate POSITION, *_POSITION, BASE -?[0-9]+,-?[0-9]+
span a set of coordinates or coordinate ranges *_SPAN a numeric, position or ([0-9]+\-[0-9]+,?|[0-9]+,?)+
alliance a set of side numbers SIDES, *_SIDES a span, or the empty string
range an attack range RANGE "melee" or "ranged"
alignment an alignment keyword ALIGN "lawful" or "neutral" or "chaotic"
types a set of unit types TYPES a shortname, name, or anything that contains spaces and matches no other type
terrain_pattern a set of terrain codes to filter ADJACENT*, TERRAINLIST*, *TERRAIN_PATTERN, RESTRICTING a terrain_code or name
terrain_code a single terrain code, perhaps with overlay TERRAIN*, *TERRAIN a shortname or (\*|[A-Z][a-z]+)\^([A-Z][a-z\\|/]+\Z)?
shortname a terrain code or a short, capitalized variable name [A-Z][a-z][a-z]?
name a name or identifier NAME, VAR, IMAGESTEM, ID, FLAG, *_NAME, *_ID, NAMESPACE, BUILDER, *_VAR anything without spaces that matches no other type
optional_string a string value (may be empty) ID_STRING, NAME_STRING, DESCRIPTION, IPF a string, or the empty string
string a nonempty string not matching any of the preceding types STRING, TYPE, TEXT, *_STRING, *_TYPE, *_TEXT a shortname, a name, a stringliteral, or anything that contains spaces and matches no other type
stringliteral a string in doublequotes or a translated string ".*" or _.* but not _[a-z].*
image an image path, perhaps with image path functions *IMAGE, PROFILE [A-Za-z0-9{}.][A-Za-z0-9_/+{}.-]*\.(png|jpg)(?=(~.*)?)
sound a music or sound filename MUSIC, SOUND string ending with ".wav" or ".ogg"
filter WML filter FILTER any non-quoted string containing "="
WML arbitrary WML fragment WML, *_WML any non-quoted string containing "=", or the empty string
affix a prefix, suffix, or infix for a variable name AFFIX, *AFFIX, POSTFIX, ROTATION a shortname or name, or the empty string
any anything *VALUE, [ARS][0-9] anything

If the actual argument is a macro call {.*}, then it matches any formal Otherwise, if the formal has an identifiable type, wmlscope will complain if the actual literal does not match it.

The argument type check only works in macro calls that fit on a single line.

wmlscope has many options for changing the reports it generates; the more advanced ones are intended for Wesnoth developers. Invocations for the most commonly useful reports it generates are included in data/tools/Makefile of the source distribution. Here are some of those reports:

make unresolved
Report on unresolved macro calls and resource references; also report macro argument-type mismatches. (This is what you are most likely to want to do).
make all
Report all macro and resource file references, not just unresolved ones.
make collisions
Report on duplicate resource files.

For more advanced users, or those who want to understand what the canned Makefile invocations are doing, here is a summary of wmlscope's options. Some of the more advanced options will require you to understand Python regular expressions.

-h, --help
Emit a help message and quit
-c, --crossreference
Report resolved macro references (implies -w 1)
-C, --collisions
Report duplicate resource files
-d, --deflist
Make definition list. (This one is for campaign server maintainers.)
-e regexp, --exclude regexp
Ignore files matching the specified regular expression.
-f dir, --from dir
Report only on macros defined under dir
-l, --listfiles
List files that will be processed
-r ddd, --refcount=ddd
Report only on macros with references in exactly ddd files.
-u, --unresolved
Report unresolved macro references
-w, --warnlevel
Set to 1 to warn of duplicate macro definitions
--force-used reg
Ignore reference count 0 on names matching regexp
--extracthelp
Extract help from macro definition comments.

wmllint

wmllint is a tool for migrating your WML to the current version. It handles two problems:

  • Resource files and macro names may change between versions of the game. wmllint knows about these changes and will tweak your WML to fit where it can.
  • Between 1.2.x and 1.3.1 the terrain-coding system used in map files underwent a major change. It changed again in a minor way between 1.3.1 and 1.3.2. If you port such old code, use wmllint-1.4, which is located in the same directory as wmllint. It will translate your maps for you, unless you use custom terrains in which case you will have to do it by hand.

wmllint also performs various sanity-checking operations, reporting:

  • unbalanced tags
  • strings that need a translation mark and do not have them
  • strings that have a translation mark and should not
  • translatable strings containing macro references
  • filter references by description= (id= in 1.5) not matched by an actual unit
  • abilities or traits without matching special notes, or vice-versa
  • consistency between recruit= and recruitment_pattern= instances
  • double space after punctuation in translatable strings.
  • unknown races or movement types in units

wmllint takes a directory-path argument specifying the WML directories to work on. It will modify any cfg and map files under those directories that need to be changed. Here is a summary of its options:

-h, --help
Emit a help message and quit.
-d, --dryrun
List changes but don't perform them.
-v, --verbose
Set verbosity; more details below.
-c, --clean
Clean up -bak files.
-D, --diff
Show diffs between converted and unconverted files.
-r, --revert
Revert the conversion from the -bak files.
-n, --nolift
Suppress lifting, do sanity checks only

The verbosity option works like this:

-v
lists changes.
-v -v
warns of maps already converted.
-v -v -v
names each file before it's processed.
-v -v -v -v
shows verbose parse details (developers only).

The recommended procedure is this:

  1. Run it with --dryrun first to see what it will do.
  2. If the messages look good, run without --dryrun; the old content will be left in backup files with a -bak extension.
  3. Eyeball the changes with the --diff option.
  4. Use wmlscope, with a directory path including the Wesnoth mainline WML, to check that you have no unresolved references.
  5. Test the conversion.
  6. Use either --clean to remove the -bak files or --revert to undo the conversion.

Additionally, wmllint tries to locate a spell checker on your system and spell-checks storyline and message strings. It will work automatically with either aspell, myspell, or ispell provided you have the enchant.py Python library installed.

wmllint supports a number of magic comments to customize its behaviour and avoid false positives. All magic wmllint comments begin with the string wmllint:, followed by some additional keyword and potentially some arguments. In the below explanations, a string of the form [a|b] means you may use either a or b at that location, while a string of the form <arg> indicates free text substitution.

  • ignore: Disables checking of terrains and translation marks on the current line only.
  • noconvert: Disables conversion of terrains and image/sound filenames on the current line only.
  • markcheck [on|off]: Enables or disables translation mark checking from the current (next?) line onward.
  • no-icon: Prevents description insertions (what does that mean?)
  • recognize <name>: Indicates that the character with the given name exists even though a declaration (ie a [unit] or [side] tag) is not visible.
  • whofield <macro> <number>: Indicates that the specified macro declares a character whose name is given by the specified argument to the macro.
  • whofield clear <macro>: Removes the whofield definition for the specified macro.
  • who <macro> is <name> <name> ...: Indicates that the specified macro declares one or more characters whose names are given as a space-separated list. This can also be used for macros that auto-recall a list of characters, some of which join later or may die at some point. If a definition for the macro already exists, then the specified names are appended to it. If a name is preceded by a double minus (-- <name>) then it is removed from the definition.
  • unwho all|<name>: Removes the who definition for the specified macro, or all who definitions.
  • usage of "<unit>" is <class>: Declares the usage of the specified unit (the usage= key in the [unit_type] tag). Useful if you are using macros to generate several similar unit types.
  • usagetype <class>: Declares a valid usage type for units. usagetypes is also recognized, and a comma-separated list of usage types can be specified.
  • validate-[on|off]: Enables or disables stack-based validation checks. Use when you have unbalanced tags in macros.
  • unbalanced-[on|off]: Similar to above, the precise difference is unclear.
  • no translatables: Suppresses warnings about a missing textdomain declaration in the current file. Make sure the file really does have no translatable strings!
  • display [on|off]: Enable or disable warnings about newlines in messages.
  • notecheck [on|off]: Enable or disable note consistency checks for unit descriptions.
  • deathcheck [on|off]: Enable or disable the check for units speaking in their death events.
  • [general|directory|local] spellings <word> <word> ...: Declares the specified space-separated list of words to be valid spellings in the specified context. The context local indicates the current file only, while directory means the current file and any siblings in the same directory or subdirectories. The global context indicates the spellings are valid anywhere.
  • no spellcheck: Disables spell checking on the current line.
  • skip-side: Indicates that there is a missing side declaration at this location that will be provided by a macro expansion.
  • match <string> with <notes_macro>: Indicates that uses of the specified string in a unit definition (usually a macro, including the curly braces) should be matched up with the specified special notes macro (which is also specified as the full macro with curly braces).
  • no ellipsecheck: Disables checking of unit ellipses on the current line.

wmlindent

Call with no arguments to filter WML on standard input to reindented WML on standard output. If arguments are specified, they are taken to be files to be re-indented in place; interrupting will be safe, as each reindenting will be done to a copy that is atomically renamed when it's done. This code never modifies anything but blank lines and leading and trailing whitespace on non-blank lines.

The indent unit is four spaces. Absence of an option to change this is deliberate; the purpose of this tool is to prevent style wars, not encourage them.

If you don't apply this tool to your own WML, the mainline-campaign maintainers will do it when and if your code is accepted into the tree.

Note: This tool does not include a parser. It will produce bad results on WML that is syntactically unbalanced. Unbalanced double quotes that aren't part of a multiline literal will also confuse it. You will receive warnings if there's an indent open at end of file or if a closer occurs with indent already zero; these two conditions strongly suggest unbalanced WML.

GUI.pyw

Starting from version 1.11.15 and 1.13.0, a GUI (written in Tkinter, plus the themed widgets ttk) is available in the same directory as the other tools. To use it, you need to have a version of Python equal to or greater than 3.1.0 (the 3.0.x series doesn't include the ttk widgets, and as such is unsuitable for this script).

If you're on Linux, be sure to have installed the python3-tk module, or the application won't run at all. To install it in a Debian-based distro (like Ubuntu), type this line in a Terminal:

sudo apt install python3-tk

To start it, just double click on the GUI.pyw file. The interface is pretty much self-explanatory, and allows you to run wmllint, wmlscope, wmlindent and wmlxgettext, modify their options, select an add-on and save the tools' output as a text file.