the

     A-A-P logo     

project


 

Recipe Examples

These are examples for what can be done with A-A-P recipes. This is not intended to teach you writing recipes, only a few things are explained. See the Aap manual for a verbose explanation. The tutorial is a good place to start learning Aap, it contains plenty of examples that are explained.

Short examples:
1. Compiling a program
2. Compiling two programs
3. Two variants of a program
4. Publishing a web site
5. A Ported application
6. Building on multiple platforms
7. Generating files
8. Version controlled sources
  Larger examples:
Building and instaling Exuberant Ctags
Maintaining a web site


Building and installing Exuberant Ctags

Exuberant ctags (ctags for short) is a nice tool for generating a tags file from source files in various languages. In many editors the tags file can be used to jump to where a function is defined, for example. It is portable to many different systems.

The original ctags distribution uses a dozen makefiles to build the program on different systems. In this example is shown how most of it can be done with just one recipe.

The main.aap recipe can be found here. You might want to open it in a separate window, so that you can look through it while reading the comments below.

More info about Exuberant Ctags can be found here.

Installing the package

Before explaining how the interesting parts of the recipe work, let's see how it can be executed. The recipe has been made available in the Aap package system. This means you can do it all with just one command:

    aap --install ctags

On many systems this will obtain the needed files and install ctags for you. However, there are a few situations when this fails:

  • One of the files cannot be downloaded. The SourceForge CVS server has been unreliable. You might want to try again later.
  • Not all systems are supported yet. In case you see the building fail and you have some idea how to fix it, please send us the fix, so that it will work for others.
  • On FreeBSD there already is a ctags command. When chosing to use the ctags port it will install the program as "exctags" instead of "ctags".
  • Lots of text scrolls by and you can't see what happened. Try editing the file $HOME/.aap/packages/ctags/AAPDIR/log (on non-Unix systems use "aap" instead of ".aap"). It should contain lots of messages that Aap produced while attempting to install ctags for you.

Building

The recipe is quite long, because it supports much more than building the program. The simple instructions for building the program are these:

       Sources = args.c asm.c asp.c awk.c beta.c c.c cobol.c eiffel.c entry.c
                 erlang.c fortran.c get.c html.c jscript.c keyword.c lisp.c
                 lregex.c lua.c main.c make.c options.c parse.c pascal.c
                 perl.c php.c python.c read.c rexx.c routines.c ruby.c
                 scheme.c sh.c slang.c sml.c sort.c sql.c strlist.c tcl.c
                 verilog.c vim.c yacc.c vstring.c
       CtagsProg = ctags$EXESUF
       ProgName = $CtagsProg

       :program $ProgName : $Sources

The $Sources variable is set to the list of source files used for the program. Note that no quotes or backslashes are used. It is easy to reformat the list of names without having to worry about special characters. It is also not necessary to make a list of object files, this is handled automatically by Aap.

The use of $CtagsProg and $ProgName to inderectly specify the name of the executable is not really needed here, but they are used in the recipe for other reasons.

Some variable names are all capitals, while others also use lower case characters. Why this difference? The all-caps variables are the ones that have a special meaning to Aap. $EXESUF, for example, is set by Aap to the suffix of executable programs. On Unix it is empty, on MS-Windows it is ".exe". To avoid trouble with using a variable that has a special meaning for Aap, lower case letters are used for normal variables.

The Aap command ":program" specifies from which source files the ctags program is build. Aap then knows enough to support:
aap    build the program
aap install   install the program
aap uninstall   uninstall the program
aap clean   cleanup most files
aap cleanmore   cleanup all generated files

Configuration

The ctags sources already come with a configure script that is used on Unix (-like) systems to adjust to the specific properties of the system. When this example was written Aap didn't have a configuration feature yet, therefore the existing configure script is used. But instead of generating a Makefile, an Aap recipe is generated.

The configure script uses "Makefile.in" to produce "Makefile". But we want it to use "config.aap.in" and produce "config.aap". This part of the recipe takes care of it:

       # Filter the configure script created by autoconf to generate config.aap
       # instead of Makefile.  This means we can use the unmodified configure.in
       # distributed with ctags.
       configure_aap : configure
           :cat configure
                   | :eval re.sub("Makefile", "config.aap", stdin)
                   >! configure_aap
           :chmod 755 configure_aap

The "configure" file is filtered and written as "configure_aap". This shell script is then executed to do the configuration. The filtering is done in three steps:

  1. The ":cat configure" command reads the "configure" file and writes it through a pipe to the next command.
  2. The ":eval" command evaluates a Python expression, where "stdin" stands for the text that is written through the pipe. The Python function "re.sub()" substitutes the string "Makefile" with "config.aap". This also works to change "Makefile.in" to "config.aap.in".
  3. The result is written into configure_aap.
After executing the filtered shell script the generated "config.aap" recipe is moved into $BDIR, so that another one is generated for other systems (using the same files over a network). The recipe is then finally included:

           :include $BDIR/config.aap

The compilation commands must be changed to specify that the generated configuration header file "$BDIR/config.h" is to be used. These commands take care of this:

           INCLUDE += -I$BDIR
           DEFINE += -DHAVE_CONFIG_H 

$INCLUDE and $DEFINE are standard variables that are used by the compile actions. Thus besides adding a value to these variables, nothing else needs to be done to make them used for the compilation.

The configure script is included with the distribution. Thus normally you don't have to generate it. But someone might need to correct the configuration method. This is done by editing "configure.in" and running the "autoconf" program. To be able to avoid running "autoconf" for most people this code is used:

       # Run autoconf when needed, but avoid doing this always, not everybody has
       # autoconf installed.  Include "mysign" in the distribution, it stores the
       # signature of the distributed configure script.
       configure {signfile = mysign} : configure.in
           @if not program_path("autoconf"):
               :print Can't find autoconf, using existing configure script.
           @else:
               :sys autoconf

The clever part is "{signfile = mysign}". This specifies a "signfile" attribute for the dependency. This means the signature for this dependency isn't stored in the usual way, but in the specified "mysign" file. This file is then included in the distribution. When Aap comes to this dependency and checks the signatures to find out if the command to build "configure" need to be executed, it will read the distributed signatures from "mysign". If both "configure" and "configure.in" are still the same as when they were distributed, the build commands will not be executed.

The build commands include an extra check if the "autoconf" program can be found. It is not installed on every system. A choice was made to skip running "autoconf" when it is not available. Another possibility would be to use ":assertpkg autoconf" to install the autoconf program when needed.

Variants

The recipe can also be used to build a debugging version of ctags. Aap has the ":variant" command to make this easy:

      :variant DEBUG
          no
              ProgName = $CtagsProg
              OPTIMIZE ?= 2
              DEBUG = no
              UNINSTALL_EXEC += $DCtagsProg	    # also uninstall dctags
              CLEANFILES += $DCtagsProg
          yes
              ProgName = $DCtagsProg
              Sources += debug.c
              DEFINE += -DDEBUG
              CPPFLAGS = -g
              OPTIMIZE ?= 0
              UNINSTALL_EXEC += $CtagsProg	    # also uninstall ctags
              CLEANFILES += $CtagsProg 

The non-debug variant is build by default, since it is the first alternative below ":variant". To build the debug version use "aap DEBUG=yes". The resulting object files will be stored in different build directories, so that you can switch between the two variants without having to recompile all files.

The standard variables $OPTIMIZE and $DEBUG are used to tell the compile action what compilation options to use. $OPTIMIZE is a number ranging from zero (no optimizing) to nine (lots of optimizing). $DEBUG is "yes" for debugging and "no" for not debugging.

Two extra assignments are used to cause "aap clean" and "aap uninstall" to also delete the files build for the variant that was not selected. $UNINSTALL_EXEC is the list of executables that are to be uninstalled. $CLEANFILES is the list of files to be cleaned up. The files normally generated for a variant do not need to be specified, these are added automatically when the ":program" command is used..

Installing etags

Etags is a program like ctags, but it produces a TAGS file with a different format. This is only useful for backwards compatibility.

Ctags actually works like Etags by either giving the "-e" argument or by calling the executable "etags". For Unix this is done by making a symbolic link from "etags" to "tags". This code in the recipe takes care of it:

      install-local:
          @if osname() == "posix":
              :cd $DESTDIR $PREFIX $EXECDIR
              :symlink {force} $ProgName $EtagsProg

      uninstall-local:
          @if osname() == "posix":
              :cd $DESTDIR $PREFIX $EXECDIR
              :del {force} $EtagsProg 

The ":cd" command used here has three arguments. These are concatenated, adding path separators where needed, to form the name of the new directory.

The ":symlink" command works like the Unix command "ln -s". The {force} attribute is used to overwrite any existing file or link.

The "install-local" and "uninstall-local" targets are used by the default install dependencies if they exist. This means you can easily add special commands for installing, like we do here, without having to write the whole "install" and "uninstall" targets.

Maintainer Functionality

The maintainer of ctags has a few tasks that normal users do not need. Therefore this has been moved to a separated recipe that is included only when needed:

      @if has_targetarg("tags TAGS tar tarclean revise cvstag"):
         :include maintainer.aap {fetch = http://www.a-a-p.org/packages/ctags_maintainer.aap}

The "has_targetarg()" function is used to check if one of the targets that requires the maintainer.aap recipe has been used. When one of these targets is present then the recipe will be downloaded from the specified URL.

You can find the maintainer.aap recipe here. Much of this recipe is listing all files that have to be distributed. This is not spectacular. More interesting is the method used to avoid having the version number in more than one place. This means that updating the version number only needs to be done once. Since the number is also needed in the recipe, it is obtained with this code:

         @r = re.compile('.*PROGRAM_VERSION "(\\d[^"]*)".*', re.DOTALL)
         :cat ctags.h | :eval r.match(stdin).group(1) | :assign Version

This compact code mixes Python and Aap commands:

  1. Compile a pattern that matches the line in ctags.h where the version is defined. A group in parentheses is used around the place where the version number is matched. The resulting object is assigned to the "r" variable.
  2. The ":cat" command reads the "ctags.h" file and pipes it to the next command.
  3. The ":eval" command uses the compiled pattern object to match with the whole file contents, found in the "stdin" variable. This results in a string with just the version number, which is piped to the next command.
  4. The ":assign" command reads the piped version number and assigns it to the $Version variable.
The version number is then used in the tar file that contains all the distributed files:

      TarFile = ctags-$(Version).tar.gz
      CLEANFILES += $TarFile

      tar {virtual} {comment = Create tar file with all distributed files}:
          # Make sure we have a tar command.
          :assertpkg tar

          # Copy the files to a new directory, so that the archive unpacks nicely.
          TarDir = ctags-$Version
          :del {f}{q}{r} $TarDir
          :mkdir $TarDir
          :copy {quiet} $DISTFILES $TarDir
          :sys tar cfz $TarFile $TarDir
          :del {f}{q}{r} $TarDir 

Most of this is easy to understand. The $DISTFILES variable contains the list of files to distribute. This is partly filled by the ":program" command in the main recipe, the compiled source files are added automatically.

The ":assertpkg" command is used to check that the "tar" program is available. If it isn't then Aap will offer you to install. This uses the Aap package mechanism.

The following code specifies which files are to be stored in the CVS server and how it's done.

       # The "commit" attribute specifies CVS is used for "aap fetch" and "aap commit".
       # The server isn't specified, use the default.
       :attr {commit = cvs://} $DISTFILES
       
       # Commit all changes into CVS.  Also deletes files!
       revise:
             :reviseall {logentry = updated for version $Version}

       # Tag the current set of files as a specific version.
       cvstag:
             :tagall {tag = ctags-`string.replace(_no.Version, ".", "_")`} 

The "commit" attribute is set to use CVS for version control. This doesn't specify a CVS server to use, which means that the default server will be used (CVS remembers the one used before). Now the this command can be used to update the files in CVS:

        aap revise 

Note that files that are omitted from $DISTFILES will be deleted from CVS, thus it is important that the list of files is correct. Well, it should be correct anyway, since the same list is used to create the tar file.

When a version is to be released the "aap cvstag" command can be used to update the tags on the files in CVS. Normally the "tag" target is used for this, but that is rather confusing in this context.

top
 
 
  The A-A-P pages:
home
news
Zimbu award
documentation
     Recipe examples
     Aap manual
     Agide tutorial
     features
     presentations
download
     Aap version log
     Agide version log
     ported apps
maillists and chat
plan and tasks
     SourceForge pages
     Aap todo list
     Agide todo list
architecture
     use cases
     modules
     interfaces
     design decisions
tools overview
     script languages
     build tools
     Install tools
     issue tracking
     version control
     browse tools
     IDEs
     various tools



OSDir.com interview about A-A-P
 
funded by:

NLnet
	 logo


Send comments on this page to Webmaster AT a-a-p.org.            Hosted by SourceForge Logo            Also known as www.Agide.org.