In various places in the recipe Python commands and expressions can be used. Python is a powerful and portable scripting language. In most recipes you will only use a few Python items. But where needed you can do just about anything with it.
When a recipe needs to work both on Unix and on MS-Windows you quickly run into the problem that the compiler does not use the same arguments. Here is an example how you can handle that.
@if OSTYPE == "posix": CPPFLAGS += -DFAST @else: CPPFLAGS += /DFAST all: :print CPPFLAGS is "$CPPFLAGS" |
The first and third line start with the "@" character. This means a Python command follows. The other lines are normal recipe lines. You can see how these two kinds of lines can be mixed.
The first line is a simple "if" statement. The OSTYPE variable is compared with the string "posix". If they compare equal, the next line is executed. When the OSTYPE variable has a different value the line below @else: is executed. Executing this recipe on Unix:
% aap
CPPFLAGS is "-I/usr/local/include -DFAST"
%
OSTYPE has the value "posix" only on Unix and Unix-like systems. Executing the recipe on MS-Windows, where OSTYPE has the value "mswin":
C:> aap
CPPFLAGS is "/IC:\VC\include /DFAST"
C:>
Note that the Python conditional commands end in a colon. Don't forget to add it, you will get an error message! The indent is used to form blocks, thus you must take care to align the "@if" and "@else" lines.
You can include more lines in a block, without the need for extra characters, such as { } in C:
@if OSTYPE == "posix": CPPFLAGS += -DFAST LDFLAGS += -L/usr/local @else: CFLAGS += /DFAST |
Python has a "for" loop that is very flexible. In a recipe it is often used to go over a list of items. Example:
1 @for name in [ "solaris", "hpux", "linux", "freebsd" ]: 2 fname = README_$name 3 @if os.path.exists(fname): 4 FILES += $fname 5 all: 6 :print $FILES |
The first line contains a list of strings. A Python list uses square brackets. The lines 2 to 4 are executed with the name variable set to each value in the list, thus four times. The indent of line 5 is equal to the @for line, this indicates the "for" loop has ended.
Note how the name and fname variables are used without a dollar in the Python code. This might be a bit confusing at first. Try to remember that you only put a dollar before a variable name in the argument of a recipe command.
In line 2 the fname variable is set to "README_" plus the value of name. The os.path.exists() function in line 3 tests if a file exists. Assuming all four files exist, this is the result of executing this recipe:
% aap
README_solaris README_hpux README_linux README_freebsd
%
When the number of Python lines gets longer, the "@" characters become annoying. It is easier to put the lines in a block. Example:
:python FILES = '' for name in [ "solaris", "hpux", "linux", "freebsd" ]: fname = "README_" + name if os.path.exists(fname): if FILES: FILES = FILES + ' ' FILES = FILES + fname all: :print $FILES |
This does the same thing as the above recipe, but now using Python commands. As usual, the :python block ends where the indent is equal to or less than that of the :python line.
When using the :python command, make sure you get the assignments right. Up to the "=" character the Python assignment is the same as the recipe assignment, but what comes after it is different.
In many places a Python expression can be used. We have already seen the use of the glob() function:
SOURCE = `glob("*.c")` |
Python users know that the glob() function returns a list of items. Aap automatically converts the list to a string, because all Aap variables are strings. A space is inserted in between the items and quotes are added around items that contain a space.
The following example turns the list of source files into a list of header files:
SOURCE = `glob("*.c")` HEADER = `aap_sufreplace(".c", ".h", SOURCE)` all: :print SOURCE is "$SOURCE" :print HEADER is "$HEADER" |
Running Aap in a directory with "main.c" and "version.c"?
% aap
SOURCE is "version.c main.c"
HEADER is "version.h main.h"
%
The "aap_sufreplace()" function takes three arguments. The first argument is the suffix which is to be replaced. The middle argument is the replacement suffix. The last argument is the name of a variable that is a list of names, or a Python expression. In this example each name in SOURCE ending in ".c" will be changed to end in ".h".
Documentation about Python can be found on its web site: http://www.python.org/doc/