Difference between revisions of "PreprocessorRef"
(→The WML preprocessor) |
(→The WML preprocessor) |
||
Line 58: | Line 58: | ||
** '''APPLE''' is stored if the computer Wesnoth is being run on is an Apple. (this one is only available to the configuration of the game) | ** '''APPLE''' is stored if the computer Wesnoth is being run on is an Apple. (this one is only available to the configuration of the game) | ||
** '''LOW_MEM''' {{DevFeature1.9}} The binary was compiled with the LOW_MEM option | ** '''LOW_MEM''' {{DevFeature1.9}} The binary was compiled with the LOW_MEM option | ||
− | ** ''' | + | ** '''TINY''' The binary was compiled with the TINY_GUI option |
* '''#ifndef''' behaves exactly like '''#ifdef''', except that it inverts the test sense; guarded text is included only if ''symbol'' has not been stored. | * '''#ifndef''' behaves exactly like '''#ifdef''', except that it inverts the test sense; guarded text is included only if ''symbol'' has not been stored. | ||
''#undef'' can be useful too if ''#ifdef'' is not enough. | ''#undef'' can be useful too if ''#ifdef'' is not enough. |
Revision as of 19:12, 20 August 2010
Contents
The WML preprocessor
Wesnoth loads just one configuration file directly: data/_main.cfg. However the WML preprocessor allows to include more files. Whenever a WML file is read by Wesnoth, it is passed through the preprocessor.
The preprocessor can interpret a simple language of string-expansions known as "macros." A macro should always be defined before the place where it needs to be used.
The preprocessor is applied recursively, so included files will be parsed for macros, and after macro expansion will be parsed for macros again, and so on. As a result, you should not write a recursive macro, because it will cause errors (but, alas, not necessarily error messages).
The following directives are used to create and use macros, i.e. shortcuts which reduce repetition of information. See UtilWML, UsefulWMLFragments for examples and the macro reference for the predefined core macros.
- #define symbol [parameters] newline substitution #enddef all subsequent occurences of {symbol [arguments]} (see below) will be replaced by substitution with all occurences of any parameter {parameter} within substitution replaced by the parameter's corresponding value in arguments. For example, the UNIT macro used below would be defined:
#define UNIT TYPE X Y## the ordering is important here; ## since WML does not distinguish ## data into different types, only ## the ordering is used to determine which ## arguments apply to which parameters. [unit] type={TYPE}## the unit will be of type TYPE, so different ## instantiations ## of this macro can create different units. x={X} y={Y} side=2## the unit will be an enemy, regardless of the parameter ## values. This reduces "repetition of information", ## since it is no longer necessary to specify ## each created unit as an enemy. [/unit] #enddef
(See SingleUnitWML for information on creating units using WML.)
- {symbol [arguments]} if symbol is defined, the preprocessor will replace this instruction by the expression symbol is defined as, using arguments as parameters. You can create multiple word arguments by using parentheses to restrain the argument. For example, while {UNIT Wolf Rider 18 24} will attempt to create a "Wolf" at (Rider,18), causing Wesnoth to crash, the macro {UNIT (Wolf Rider) 18 24} will create a "Wolf Rider" at (18,24) as it should. (UNIT is defined above). See the #define preprocessor instruction above for information on defining symbols, including symbols with arguments. Several symbols are defined in the normal game code; for reference they are listed in UtilWML.
Note: Using the name-string of an existing macro as the name-string of a macro argument will always overwrite the original definition of the macro, e.g.:
#define VARIABLE #enddef #define MACRO VARIABLE {VARIABLE} # is calling for the argument, not for the macro above #enddef
Unlike the other preprocessor directives, #ifdef and #ifndef are not
mere conveniences.
They are necessary to distinguish between different modes of play.
- #ifdef symbol substitution-if-stored [#else substitution-if-not-stored] #endif If symbol has been stored, the whole block will be replaced by substitution-if-stored. If not, it will be replaced by substitution-if-not-stored if it is available. symbol can take the following values:
- a campaign difficulty level. Usually EASY, NORMAL, or HARD, it is decided by the key difficulties (see CampaignWML).
- a campaign ID. Each campaign stores a preprocessor ID. See define, CampaignWML.
- MULTIPLAYER is stored when the player is playing or setting up a multiplayer game (i.e. a game with scenario_type=multiplayer).
- DEBUG_MODE is stored when the player has launched wesnoth in debug mode (i.e. with -d)
- TUTORIAL is stored when the player is playing the tutorial.
- APPLE is stored if the computer Wesnoth is being run on is an Apple. (this one is only available to the configuration of the game)
- LOW_MEM Template:DevFeature1.9 The binary was compiled with the LOW_MEM option
- TINY The binary was compiled with the TINY_GUI option
- #ifndef behaves exactly like #ifdef, except that it inverts the test sense; guarded text is included only if symbol has not been stored.
#undef can be useful too if #ifdef is not enough.
- #undef symbol erases symbol
- #ifhave Template:DevFeature1.9 checks for the existence of a file.
- #ifnhave Template:DevFeature1.9 is the inverted version of #ifhave.
These directives expand a file by including other files, filename may not contain '..' or the directive will be skipped:
- {filename} if filename isn't a predefined symbol (see above), Wesnoth will assume it's a path to a file in the main data/ subdirectory of Wesnoth and include the file it points to here "as is". Forward slashes(/) should be used to separate directories from their elements, even if your platform uses a different symbol such as colon (:) or backslash (\).
- {~filename} similar to above, but the preprocessor assumes that the filename is relative to data/ subdirectory of the user data directory. The user data directory varies depending on platform. See Where_is_my_user_data_directory?
- {./filename} is similar to the {filename} directive, but is assumed to be relative to the directory which contains the file in which this appears. For example, if used in game.cfg, it is equivalent to {filename}.
- If filename points to a directory, the preprocessor will include all files in the directory filename, non-recursively and in alphabetical order. Starting in 1.3.3, some files in such directories are handled specially.
If there is a file named dir/_main.cfg in a directory referenced as {dir}, only that _main.cfg file is processed; it may include other files in itself, of course . This feature is useful for creating WML directories that are self-contained WML packages with their own initializations (like campaigns). This can replace the older convention of having a dir.cfg at the same level as dir which does {dir} somewhere in itself.
If there are files named dir/*/_main.cfg in a directory referenced as {dir}, where * is any subdirectories dir, then they all are processed (except if there is also a file named dir/_main.cfg). This means that if you have a layout like this:
dir/ dir/a/_main.cfg dir/a/other.cfg dir/b/_main.cfg dir/b/other.cfg dir/other.cfg
Then {dir} will process (in alphabetical order): dir/a/_main.cfg, dir/b/main.cfg, dir/other.cfg.
If there is a file named dir/_final.cfg in a directory referenced as {dir} (and no main.cfg, so that all files in the directory are processed normally) the _final.cfg is guaranteed to be processed after all other files in the directory.
If there is a file named dir/_initial.cfg in a directory referenced as {dir} (and no main.cfg, so that all files in the directory are processed normally) the _initial.cfg is guaranteed to be processed before all other files in the directory.
Note that the parser was changed various times, so don't expect older Wesnoth version to behave exactly the same.
Template:DevFeature1.9 Including non-existent file is now a fatal error and halts the preprocessing. Use #ifhave to test for the existence of files that aren't a part of your own add-on.
The preprocessor command line
As of wesnoth 1.9, there are some new command line arguments available for the wesnoth executable: --preprocess --preprocess-input-macros --preprocess-output-macros
Commands details:
--preprocess
--preprocess[=define1,define2,...] <file/folder> <target directory>
or the short form:
-p[=define1,define2,...] <file/folder> <target directory>
The define1,define2,... is a list of defines, to be added before anything is preprocessed.
The preprocess command will preprocess first the common config files in data/core, and afterwards, the specified ones. You can specifiy a single file to be preprocesses (if you want to preprocess multiple separate files, you have to invoke the command separately), or an entire folder (this will be preprocessed on the known preprocessor rules).
The resulted preprocessed files will be written in the target directory. There will be 2 types of files: the .cfg files and .plain files (this contain the line symbols and textdomain changes)
If <file/folder> and <target directory> are not in absolute path form, they will be treated like relative to the wesnoth's executable path.
Some examples:
-p ~/wesnoth/data/campaigns/tutorial ~/result
will preprocess entire tutorial folder, and write the resulted files in the ~/result folder
-p=MULTIPLAYER data/campaigns/my_campaign/some_scenario.cfg data/result
will add the MULTIPLAYER define in defines list and then preprocess the scenario config file
-p=SINGLEPLAYER,HARD ~/wesnoth/data/campaigns/my_campaign ~/result
will add the SINGLEPLAYER and HARD defines before preprocessing the files.
Note! As you saw in the examples, the '=' character is mandatory if you want to specify any auxiliar defines
--preprocess-input-macros
--preprocess-input-macros <file>
This command will take the specified file, parse the defines in it, and will use them when preprocessing the resource with --preprocess. The file must contain only [preproc_define]s. This is different than adding defines to --preprocess, as it contains the macro information.
--preprocess-output-macro
--preprocess-output-macro [<file>]
Outputs the defines_map resulted from preprocessing. This is will include (if any) --preprocess-input-macro file's content.
If you want a more detailed log, you can simply add the command: "--log-debug=all" or "--log-info=all" before the preprocessing command, so you will see how things are parsed,etc.