Legacy:UCPP - Unreal Wiki

# Legacy:UCPP

### From Unreal Wiki, The Unreal Engine Documentation Site

UnrealScript Class PreProcessor (UCPP) is a commandline utility intended to be executed before `ucc make`. It adds some preprocessor features that are not possible with Ucc. UCPP takes special class files with a `.puc` extention, processes them and generates a `.uc`.

El Muerte started on this tool to add some more magic to an upcoming UnrealScript unit testing framework (UsUnit). This tool is currently in development; any help (like testing) would be greatly appreciated.

## Features

### Directives

directive | description
`#define name something ...` | adds\overrides an definition name (case insensitive) with the value something ... (till the end of the line)
`#undef name` | removed name from the definition list (for this file only)

`#if expresion`
...
`#elif expresion`
...
`#else`
...
`#endif`

expression contains an expression constructed from numbers or defined names. The `#else` is optional. `#elif` (since version 009) is short of "else if", you can have as many of these as you want. You can nest these.

`#ifdef name` | evaluates to true if name is defined. Can also be used to check if function definitions have been made: `#ifdef MYFUNC/2`.
`#ifndef name` | the opposite of `#ifdef`
`#include filename` | include the file in the output file, this does the same as the #include directive in UE2. By default this directive is disabled, you can enable it in the config file.
`#ucpp command` | various UCPP related items; usually these will be replaced with comments. The following commands are accepted.
| `notice` | this will add a notice that the uc file was generated by UCPP, everything after this command is ignored.
| `version` | this will add a comment with the UCPP version info and homepage link.
| `include filename` | include filename to be processed for macros, nothing else will be done with the file. It has the same effect as using the command argument -imacros filename. The filename is relative to the currently being processed file.
| `error message` | produces a preprocessor error.
| `warning message` | produces a preprocessor warning. If the message is off warning reporting will be turned off, and on again if it is on (and nothing more),
| `rename filename` | renames the resulting file to filename. This can be useful to use UCPP to create include files to be used with the UnrealEngine's `#include` directive
| `config variable value` | this will override certain configuration variables for this file only. Variable can be one of the following: supportIf, supportDefine, supportPreDefine, supportInclude, stripCode. Value can be true, false, 0, 1 or empty to reset to the default value.
`#pragma ucpp command` | Accepts the same commands as the normal `#ucpp` directive except that using `#pragma ucpp` is more portable (in case you ever switch preprocessors).

All macros that UCPP processed will simply be commented out.

Most macros allow comments on the same line; everything after the comment start token (`//` or `/*`) will be removed. The macro `#define` does not support comments. For example:

    #if 1 // always evaluates to 'true'
    #endif /* this comment is also ignored */

    #define INC_COMMENT log("Bogus"); // this comment is included with the definition

    #ifdef 1 /* always evaluates to false */ && 0

Note: in UCPP version 1.3 and earlier you could not have spaces between `#` and the directive. Also block everything was removed from the start of a block comment.

Note 2: Do not start a block comment in a macro and end it on an other line. This will break your code even tho UCPP accepts it. For example, the following is bad:

    #if 1 /*
        a block comment
    */
    #endif

#### Expression syntax

Grammar:

     EXPR      ::= CMPX
     CMPX      ::= ORX ( CMPOP CMPX )*
     ORX       ::= ACCUM ( '||' ORX )*
     ACCUMX    ::= ANDX ( '+'|'-' ACCUM )*
     ANDX      ::= MULTX ( '&&' ANDX )*
     MULTX     ::= UNARYX ( '*'|'/' MULTX )*
     UNARYX    ::= ( '!' )? OPERAND
     OPERAND   ::= LVALUE | '(' EXPR ')'
     LVALUE    ::= integer | BUILTIN '(' EXPR ')' | IDENTIFIER

     CMPOP     ::= '<' | '<=' | '=<' | '=>' | '>=' | '>' | '==' | '!='
     BUILTIN   ::= 'defined' | 'strcmp' | 'stricmp' | ...

Operator precedence – from top to bottom
!
/, *
&&
, -
{| |- | |}
<, <=, =<, =>, >=, >, ==, !=

Note: `<=` and `=<=` are the same, and so are `>=` and `=>=`

`IDENTIFIER` is resolved in the definition table. If it is not defined, the entire expression will fail (e.g. the result is false). An empty definition (`#define DEBUG`) will evaluate to `1`.

The following examples are valid expressions:

    #if DEBUG && UNSTABLE_CODE
    #endif

    #if !DEBUG || (STABLE_TEST_CODE && COOKIES)
    #endif

There are a couple of functions that can be used in an expression:

function | description
`defined(name)` | Returns 1 if name is defined, 0 otherwise
`strcmp(name or string, name or string)` | Returns 0 if both strings are equal, you can use an identifier or a string constant. (v103 and up)
`stricmp(name or string, name or string)` | Identical as `strcmp` but case insensitive. (v103 and up)

#### Examples: defined(name)

    #undef DEBUG

    #if defined(DEBUG)
      // this is accepted
    #endif

    #if DEBUG
      // this will produce a preprocessor error
    #endif

#### Examples: strcmp(arg1, arg2)

    #define TEST1 "This is a test"
    #define TEST2 "This is a test"

    #if strcmp(TEST1, TEST2) == 0
      evaluates to true
    #endif

    #if strcmp(TEST1, TEST2)
      result is 0 so it evaluates to false
    #endif

    #if stricmp(TEST1, "This Is A Test") == 0
      evaluates to true
    #endif

    #define TEST4 12345
    #if strcmp(TEST4, 12345) == 0
      because of a side effect this also evaluates to true
    #endif

    #define TEST5 thisIsATest
    #if strcmp(TEST5, "thisIsATest") == 0
      not the same, one is a string, the other isn't, so -> false
    #endif

### Macros\Identifiers

All source code will be parsed for uppercase identifiers (like `SOMETHING` or `SOMETHING_ELSE`). These will then be replaced by their definition (if defined) AS IS. This means that `"this is a value of a definition"` will be inserted into the code like that.

You can add new definitions via the macro `#define` on the commandline or in the config file.

#### Pre-defined macros

The following names are always defined; some have some additional magic (with the name `__NAME__`). You can never override these magic definitions, however you can disable them by using the `-undef-` commandline argument.

`__FILE__` | The full filename of the current file surrounded by double quotes (the generated `.uc` files).
`__FILE_BASE__` | Just the filename without the directory name, also surrounded by double quotes.
`__CLASS__` | The filename without an extention, should be equal to the class name. This isn't quoted. (006 and up)
`__LINE__` | The current line in the code, starting from 1.
`__DATE__` | Returns the current date in the long format (as a string), as defined by the current locale settings.
`__TIME__` | Returns the current time in the long format (as a string), as defined by the current locale settings.
`UCPP_VERSION` | The UCPP version number.
`UCPP_HOMEPAGE` | The UCPP homepage, the URL of this page.
`CLASS_classname` | Where classname is the name of the current class. This is an empty define, it can be useful in include files. (007 and up)

### Function Defines

As of version 006 beta it is possible to define macro functions.

    #define FUNC(a,b,c) log(a$__FILE__, c); a = b;
    //      ^           ^- implementation
    //      \- definition

The above is a definition of the function `FUNC/3` Note that this is the so called footprint of the function, the number reflects the number of arguments. It's a function with the name `FUNC` that accepts 3 arguments. The definition part may not contain any spaces, since the space defines where the implementation part starts.

Function arguments must be seperated by commas. Each argument must have an unique name. The implementation part is an unrealscript code snippet that contains the function arguments or anything else (except preprocessor commands). The resulting implementation is parsed again to process all defines used in the implementation.

Function definitions are unique by their number of arguments. `FUNC/2` does not replace `FUNC/3`. In this way you will be able to overload functions. The following definitions have the same footprint, thus the last one will be come effective:

    #define FUNC(a,b,c) log(a$__FILE__, c); a = b;
    #define FUNC(foo,bar,quux) // every call will be replaced by this comment

There are two special tokens you can use in the implementation part:

token | meaning
`#` | Quote the next function argument (make it an valid unrealscript string): `# a -> "a"`. This only affects function arguments, if the next token isn't a argument it will not be quoted: `# NotAnArgument -> # NotAnArgument` (no substitution at all).
`##` | This will concat the previous argument with the next part: `a ## b -> ab` and `a ## NotAnArgument -> aNotAnArgument`, but also `NotAnArgument ## a -> NotAnArgumenta`. Note: in 006 beta this doesn't work as it should, it is fixed in version 007.

In order to check if a certain function is defined use the footprint:

    #ifndef FUNC/3
      #define FUNC(a,b,c) log(a$__FILE__, c); a = b;
    #endif

You can not use these functions in an `#if` expression.

### Commandline options

    Usage:
      ucpp.exe [switches] [settings] <name> <name> ...

      <name> is the name of a file or package, depeding on the mode.

    Switches (case sensitive):
      -?    This message
      -D<name>=<value>
            Define a <name> with a <value>. This overrides the standards as
            defined in the configuration file
      -env  Import environment variables as declarations
      -imacros <filename>
            Process <filename> for macros. This will be done for every file
            that needs to be processed
      -include <filename>
            Alias for -imacros
      -L    Print the program's license
      -undef
            Do not use predefinitions (like __FILE__)
      -P    Enable package mode. The provided names are package names.
            In package mode the base directory must be defined using
            either the SYSTEM or BASE setting
      -pipe
            Read from the stdin and write to the stdout. Only works for
            single files and not in package mode.
      -q    Be quite, only show errors\warnings
      -stdout
            Similar to -pipe except that the file is not read from the
            standard in, but from the file provided on the commandline.
            So it will just write to the standard output.
      -strip
            Strip the code instead of commenting it out
      -U<name>
            Undefine <name>. Note: this has a lower precedence than
            definitions made in the file
      -V    Show the program version
      -wait Pause at the end of executiong when there where errors
      -WAIT Always pause at the end of processing

    Settings are always in the format "<Key>=<Value>". The following
    keys are accepted:
      BASE          The base directory of the game. Required in
                    Package Mode, unless SYSTEM is already given.
      CONFIG        Alternate configuration file to use.
      MOD           The mod name, as used in the enhanced mod
                    architecture.
      SYSTEM        The "System" directory of the game. Required in
                    Package Mode, unless BASE is already given.

If an error was produced the exit value will be not zero.

It's easy to configure WOTgreal to execute the preprocessor before compiling the selected packages. The commandline you should use is:

    ucpp.exe SYSTEM=%SYSTEMDIR% MOD=%CURRENTMOD% -DDEBUG=%DEBUGCOMPILE% -P %SELECTEDPACKAGES%

This will automatically define the identifier DEBUG either set to 0 or 1 depending on if you made a debug compile in WOTgreal. Note: with this `DEBUG` is always defined, use it with `#if` not with `#ifdef`.

## Configuration

UCPP has a few configurable options. Unless `CONFIG=filename` is specified the program will load `ucpp.ini` in the same directory as the location oc `ucpp.exe`. You can set the following items:

    [Options]
    supportIf=1
    supportDefine=1
    supportPreDefine=1
    supportInclude=0
    stripCode=0
    stripMessage=// UCPP: code stripped
    noticeMessage=// NOTICE: This file was automatically generated by UCPP; do not edit this file manualy.

supportIf (*)
  toggles the support for `#if ... #elif ... #else ... #endif` and `#ifdef` directives. This way the original compiler should take care of these directives (Only useful for the games of Irrational: Tribes: Vengeance and SWAT4).
supportDefine (*)
  toggles support for `#define` and `#undef`
supportPreDefine (*)
  when turned off it has the same effect as `-undef-` on the commandline (commandline will override this)
supportInclude (*)
  enable support for the `#include` macro.
stripCode (*)
  instead of commenting out the code it will be removed, works the same as the `-strip-` commandline options (commandline will override this)
stripMessage
  the message to use when stripping the code, if you leave it blank it only the whitespace will remain.
noticeMessage
  the message to use when the `#ucpp notice` directive is used.

(*): these settings can be changed through the `#pragma ucpp config` directive.

    [Defines]
    NAME=Value

The same as `#define NAME Value`. You can use this for global definitions, although using an include file has more possibilities.

## UnrealEngine Licensee Notice

Since version 1.4 there is a new feature that allows you to pipe the source file through UCPP. With this feature it is possible to hook up UCPP with the UnrealScript compiler. This way you don't need special `.puc` files, when the engine imports the code from the `.uc` file it can pipe it through UCPP. This way the preprocessing will be done on the fly.

For more information on how to add external preprocessor support to the UnrealEngine you can contact me through the UDN IRC Server, my nickname is elmuerte (obviously).

## Downloads

- Version 1.6 - release notes

- Version 1.5 - release notes
- Version 1.4 - release notes
- Version 1.3 - release notes
- Version 1.2 - release notes
- Version 1.1 - release notes
- Version 1.0
- Version 008 beta
- Version 007 beta
- Version 006 beta
- Version 005 beta

Since UCPP uses some parts of UnCodeX it is included with the UnCodeX project on SourceForge. UCPP's source code is available in the UnCodeX source in the directory `src\ucpp`.

## Known bugs\issues

Newlines are not supported
  There is no way to insert a newline via definitions or macros. The reason for this is simple: the line numbers of the puc and uc file won't be skewed. This way it's easier to find compiler errors.

#include files not processed
  Files included via the UnrealEngine built-in #include macro are not processed for macros. Use `#ucpp include filename` to include preprocessor directives. It is possible to enable support for include directives, in this case the `#include` line is replaced with the actual file during rewriting. So the actual unrealscript compiler won't have to include it.

Code obliviousness
  the preprocessor is completely oblivious about the actual unrealscript source code it preprocesses. This means definitions made in super classes do not exist in subclasses. For global definitions you should use other means like the configuration file, the commandline or an include file (`#ucpp include file`).

Recursive defines will break
  `#define A(x) B(x) #define B(x) A(x)` will break. In 007 and later an error will be generated.

Function defines can not be used in expressions
  You can not use function defines in `#if` expressions, however you can use them in `#ifdef`, but you will need to reference them by their footprint: `FUNC(a,b,c)` has the footprint `FUNC/3`

## Feature requests

Please add your feature requests here, or on the SourceForge project page

Xian: Maybe add __USER__ and __PROJECT__, them being config-ed in the INI. Maybe if user is empty, use the current windows user, if the project macro is empty, use the Folder we're working in (not the package foler, i.e. C:\MyMod\MyPkg\Classes, it will use "MyMod"). Perhaps some code support too, such as checking if a var/func is replicated and/or if a var name/function are defined in the .uc file (unless #ifdef MYFUNC/2 does this and I misunderstood). And last but not least, an option to comment out only parts (and remove the other code if stripping is enabled) with some possible custom comment macros.

El Muerte: if you need a __USER__ and __PROJECT__ then use the standard define functionality already provided. Additionally you can import all evironment variables as defines using the -env commandline argument. In that case USERNAME will expand to the current username. PreProcessors are oblivious to the actual code, it doesn't know about anything related to replication. The #ifdef MYFUNC/2 tests if there is a macro defined with that signature, it has nothing to do with the unrealscript code.

Xian: Thanks for the reply. I see, I'll check the environ define. Ah, so it's basically just generation occurring, not parsing at all (of the UScript code, that is). Well, not so bad me thinks sine that might complicate things due to inheritance... Thanks for the tool, though, it's amazing :)

## Tips and Tricks

### Mixing defines and actual functions

Since only uppercase identifiers will be replaced and because unrealscript is case insensitive you can mix them to add extra functionality.

    #define LOG(a,b) log(a$chr(10)$__FILE__$":"$string(__LINE__), b)
    #define LOG(a) LOG(a, name)

Now if you use the `LOG` (in uppercase) function it will append the source file and the line number. If the file isn't processed by UCPP it will still work (taking into account that the above macros where defined somewhere else than in the source file).

### Using the current working directory

If you want to use the current working directory as BASE or SYSTEM just use a ".". BASE and SYSTEM take relative paths, and "." means the current directory. So for example:

    c:\UT2004\System>ucpp -P SYSTEM=. MyPackage

The following does the same

    c:\UT2004\System>ucpp -P BASE=.. MyPackage

## Comments

Tarquin: Sounds interesting!

El Muerte: Aargh, about 30 minutes after I released 007 I found a minor yet stupid bug. Backslashes are not properly escaped. Not a major thing, but just annoying.

El Muerte: There, another release. Implemented pretty much every feature I wanted. Still have to document how to use the config file. But for the rest if this release holds up I think a stable release is close.

El Muerte: ok, here's version 1.0, did quite some testing a found a few minor bugs, also new in the release is support for `#elif`. I'm delaying the press release a bit, just in case of an serious bug.

Guest: Featured on BU's news page. :) http://www.beyondunreal.com/daedalus/singlepost.php?id=8449

razialx: El Muerte, you are amazing. Now I feel like starting up coding in UScript again.

Devi: We've just started using this on the game my company is working on (I'm under an NDA, so I can say no more I'm afraid) and I'd just like to say: "Thankyouthankyouthankyouthankthankyou, you have made our lives SO much easier" :)

Jon: Soon you'll be speaking in binary code and taking over all our satellites. :)

Category: Legacy Application

##### Views

- Legacy
- Discussion
- Current revision

##### Search