<!--  vim: set sw=2 sts=2 et ft=docbk:

  Part of the A-A-P recipe executive: Tutorial - Using Python

  Copyright (C) 2002-2003 Stichting NLnet Labs
  Permission to copy and use this file is specified in the file COPYING.
  If this file is missing you can find it here: http://www.a-a-p.org/COPYING

-->

<para>
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.
</para>

<bridgehead>Expressions</bridgehead>

<para>
(Almost) anywhere you have a value, such as a text string, you
can use a Python expression instead. For instance, you could use a 
Python expression to retrieve the value of an environment variable
for use in a recipe, or use an expression to compute some
strange value.
</para>

<para>
Expressions are written between backticks (`&nbsp;`)
and must be valid Python expressions.
Some examples:
</para>

<programlisting>
1     myhome = `os.environ.get("HOME")`
2     label = `"L"+str(17*22)`
</programlisting>

<para>
The first example line shows how to retrieve an environment variable
by using Python's built-in <literal>os.environ</literal> module.
The second shows how you can use Python to compute something
within an &Aap; recipe. It doesn't do anything useful, but it
uses Python to compute the value <literal>L374</literal>,
and then &Aap; assigns that value to the variable <literal>label</literal>.
</para>

<note>
Using environment variables is probably not portable.
</note>

<bridgehead>Conditionals</bridgehead>

<para>
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.
</para>

<programlisting>
1     @if OSTYPE == "posix":
2         INCLUDE += -I/usr/local/include
3     @else:
4         INCLUDE += -Ic:/vc/include
5
6     all:
7         :print INCLUDE is "$INCLUDE"
</programlisting>

<para>
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.
</para>

<para>
The first line is a simple "if" statement.  The <literal>OSTYPE</literal> variable is
compared with the string "posix".  If they compare equal, the next line is
executed.  When the <literal>OSTYPE</literal> variable has a different value the line
below <computeroutput>@else:</computeroutput> is executed.  Executing this recipe on Unix:
</para>

<literallayout>    % <userinput>aap</userinput>
    INCLUDE is "-I/usr/local/include"
    %
</literallayout>

<para>
<literal>OSTYPE</literal> has the value "posix" only on Unix and Unix-like systems.
Executing the recipe on MS-Windows, where <literal>OSTYPE</literal> has the value
"mswin":
</para>

<literallayout>    C:> <userinput>aap</userinput>
    INCLUDE is "-Ic:/vc/include"
    C:>
</literallayout>

<para>
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.
</para>

<para>
You can include more lines in a block, without the need for extra characters,
such as { } in C:
</para>

<programlisting>
      @if OSTYPE == "posix":
          INCLUDE += -I/usr/local/include
          LDFLAGS += -L/usr/local
      @else:
          INCLUDE += -Ic:/vc/include
          LDFLAGS += -Lc:/vc/lib
</programlisting>


<bridgehead>Scope</bridgehead>

<para>
In Aap commands a variable without a scope is searched for in other scopes.
Unfortunately, this does not happen for variables used in Python.  To search
other scopes you need to prepend "_no." before the variable name.  Changing
the above example to print the result from Python:
</para>

<programlisting>
      @if OSTYPE == "posix":
          INCLUDE += -I/usr/local/include
      @else:
          INCLUDE += -Ic:/vc/include

      all:
          @print 'INCLUDE is "%s"' % _no.INCLUDE
</programlisting>


<bridgehead>Loops</bridgehead>

<para>
Python has a "for" loop that is very flexible.  In a recipe it is often used
to go over a list of items.  Example:
</para>

<programlisting>
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
</programlisting>

<para>
The first line contains a list of strings.  A Python list uses square
brackets.  The lines 2 to 4 are executed with the <literal>name</literal> variable
set to each value in the list, thus four times.  The indent of line 5 is equal
to the <computeroutput>@for</computeroutput> line, this indicates the "for" loop has ended.
</para>

<para>
Note how the <literal>name</literal> and <literal>fname</literal> variables are used without
a dollar in the Python code.  You only put a dollar before a variable name in
the argument of an Aap command.  Not in Python code and not on the left hand
side of an assignment.
</para>

<para>
In line 2 the <literal>fname</literal> variable is set to "README_" plus the value of
<literal>name</literal>.  The <computeroutput>os.path.exists()</computeroutput>
function in line 3 tests if a file exists.  Assuming all four files exist,
this is the result of executing this recipe:
</para>

<literallayout>    % <userinput>aap</userinput>
    README_solaris README_hpux README_linux README_freebsd
    %
</literallayout>


<bridgehead>Python Block</bridgehead>

<para>
When the number of Python lines gets longer, the "@" characters become
annoying.  It is easier to put the lines in a block.  Example:
</para>

<programlisting>
    :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
</programlisting>

<para>
This does the same thing as the above recipe, but now using Python commands.
As usual, the <computeroutput>:python</computeroutput> block ends where the
indent is equal to or less than that of the
<computeroutput>:python</computeroutput> line.
</para>

<para>
When using the <computeroutput>:python</computeroutput> 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.
</para>


<bridgehead>Expressions for Files</bridgehead>

<para>
In many places a Python expression can be used.  For example, the
<computeroutput>glob()</computeroutput> function can be used to expand wildcards:
</para>

<programlisting>
   Source = `glob("*.c")`
</programlisting>

<para>
Python users know that the <computeroutput>glob()</computeroutput> 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.
</para>

<note>
  <title>Using glob() is dangerous</title>
<para>
It is actually a bit dangerous to get the list of source files with the
<computeroutput>glob()</computeroutput> function, because a "test.c" file that you
temporarily used will accidentally be included.  It is often better to list
the source files explicitly.
</para>
</note>

<para>
Why use <computeroutput>glob()</computeroutput> when you can use wildcards directly?
The difference is that the expansion with <computeroutput>glob()</computeroutput> takes
place immediately, thus $Source will get the expanded value.  When using
wildcards directly the expansion is done when using the variable, but that
depends on where it is used.  For example, the
<link linkend="cmd-print">:print</link> command does not do
wildcard expansion:
</para>

<programlisting>
   pattern = *.c
   expanded = `glob(pattern)`
   all:
       :print pattern $pattern expands into $expanded
</programlisting>

<para>
When "foo.c" and "bar.c" exist, the output will be:
</para>

<literallayout>    % <userinput>aap</userinput>
    pattern *.c expands into foo.c bar.c
    %
</literallayout>

<para>
The following example turns the list of source files into a list of header
files:
</para>

<programlisting>
    Source = `glob("*.c")`
    Header = `sufreplace(".c", ".h", Source)`
    all:
        :print Source is "$Source"
        :print Header is "$Header"
</programlisting>

<para>
Running &Aap; in a directory with "main.c" and "version.c"?
</para>

<literallayout>    % <userinput>aap</userinput>
    Source is "version.c main.c"
    Header is "version.h main.h"
    %
</literallayout>

<para>
The <link linkend="python-sufreplace">sufreplace()</link> 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 <literal>Source</literal> ending in ".c" will be changed
to end in ".h".
</para>

<bridgehead>Further Reading</bridgehead>

<para>
  The User manual <xref linkend="user-python"/> has more information.
  Documentation about Python can be found on its web site:
<ulink url="http://www.python.org/doc/">http://www.python.org/doc/</ulink>
</para>

