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

  Part of the A-A-P recipe executive: Version Control

  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>
A common way to distribute sources and working together on them is using CVS.
This requires a certain way of working.  The basics are explained here.  For
more information on CVS see http://www.cvshome.org.
</para>


<bridgehead>Obtaining A Module</bridgehead>

<para>
The idea is to hide the details from a user that wants to obtain the module.
This requires making a toplevel recipe that contains the instructions.  Here
is an example:
</para>

<programlisting>
        CVSROOT = :pserver:anonymous@myproject.cvs.sourceforge.net:/cvsroot/myproject
        :child mymodule/main.aap {fetch = cvs://$CVSROOT}
        all fetch:
            :fetch {fetch = cvs://$CVSROOT} mymodule
</programlisting>

<para>
  Executing this recipe will use the "fetch" target.  The
  <link linkend="cmd-fetch">:fetch</link> command
takes care of checking out the whole module "mymodule" from the CVS repository
with the specified name.
</para>

<para>
Note that this toplevel recipe cannot be obtained from CVS itself, that has a
chicken-egg problem.
</para>


<bridgehead>Fetching</bridgehead>

<para>
The child recipe "mymodule/main.aap" may be totally unaware of coming from a
CVS repository.  If this is the case, you can build and install with the
recipe, but not fetch the files or send updates back into CVS.  You need to
use the toplevel recipe above to obtain the latest updates of the files.  This
will then update all the files in the module.  However, the toplevel recipe
itself will never be fetched.
</para>

<para>
To be able to fetch only some of the files of the module, the recipe must be
made aware of which files are coming from CVS.  This is done by using an
"fetch" attribute with a URL-like specification for the CVS server: {fetch
= cvs://servername/dir}.  Since CVS remembers the name of the server, leaving
out the server name and just using "cvs://" is sufficient.  Example:
</para>

<programlisting>
        source = foo.c version.c
        header = common.h
        :attr {fetch = cvs://} $source $header
        :program myprogram : $source
</programlisting>

<para>
If you now do "aap fetch" with this recipe, the files foo.c, version.c and
common.h will be updated from the CVS repository.  The target myprogram isn't
updated, of course.
</para>

<para>
Note: When none of the used recipes specifies a "fetch" target, one will be
generated automatically.  This will go through all the nodes used in the
recipe and fetch the ones that have an "fetch" attribute.
</para>

<para>
The recipe itself may also be fetched from the CVS repository:
</para>

<programlisting>
        :recipe {fetch = cvs://}
</programlisting>

<para>
To update a whole directory, omit the "fetch" attribute from individual files
and use it on the directory.  Example:
</para>

<programlisting>
        source = main.c version.c
        :attr {fetch = cvs://} .
        :program myprog : $source
</programlisting>

<para>
Alternatively, a specific "fetch" target may be specified.  The automatic
updates are not used then.  You can specify the "fetch" attribute right
there.
</para>

<programlisting>
        fetch:
            :fetch {fetch = cvs://} $source
</programlisting>

<para>
If you decided to checkout only part of a module, and want to be able to get
the rest later, you need to tell where in the module of the file can be found.
This is done by adding a "path" attribute to the cvs:// item in the fetch
attribute.  Example:
</para>

<programlisting>
        fetch:
            :fetch {fetch = $CVSROOT {path = mymodule/foo}} foo.aap
</programlisting>

<para>
What will happen is that aap will checkout "mymodule/foo/foo.aap", while
standing in two directories upwards.  That's required for CVS to checkout the
file correctly.  Note: this only works as expected if the recipe is located in
the directory "mymodule/foo"!
</para>
<para>
If the "path" attribute is omitted, A-A-P will obtain the information from the
"CVS/Repository" file.  This only works when something in the same directory
was already checked out from CVS.
</para>


<bridgehead>Checking In</bridgehead>

<para>
When you have made changes to your local project files and want to upload them
all into the CVS repository, you can use this command:
</para>

<programlisting>
        :reviseall
</programlisting>

<para>
You must make sure that _ALL_ files in the current directory and below that
you want to appear in CVS have the "commit" attribute, and no others!  Files
that were previously not in CVS will be added ("cvs add file") and that exist
in CVS but don't have a "commit" attribute are removed ("cvs remove file").
Then all files are committed ("cvs commit file").
</para>

<para>
  For CVS you need to mark binary files specifically, otherwise checking out
  may result in a wrong file (esp. on MS-Windows).  In Aap this is done by
  adding the "binary" attribute to binary files.  Example:
</para>

<programlisting>
        Files = main.c version.c logo.png {binary}
</programlisting>

<para>
To be able to commit changes you made into the CVS repository, you need to
specify the server name and your user name on that server.  Since the user
name is different for everybody, you must specify it in a recipe in your
~/.aap/startup/ directory.  For example:
</para>
        
<programlisting>
        CVSUSER_AAP = foobar
</programlisting>

<para>
The name of the variable starts with "CVSUSER" and is followed by the name of
the project.  That is because you might have a different user name for each
project.
</para>

<para>
The method to access the server also needs to be specified.  For example, on
SourceForge the "ext" method is used, which sends passwords over an SSH
connection for security.  The name used for the server then becomes:
</para>

<programlisting>
        :ext:$CVSUSER_AAP@cvs.a-a-p.sf.net:/cvsroot/a-a-p
</programlisting>

<para>
You can see why this is specified in the recipe, you wouldn't want to type
this for commiting each change!
</para>


<bridgehead>Distributing Your Project With CVS</bridgehead>

<para>
This is a short how-to that explains how to start distributing a set of files
(and directories) using CVS.
</para>


<orderedlist>
<listitem>
<para>
Copy the files you want to distribute to a separate directory
</para>

<para>
Mostly you have various files in your project for your own use that you
don't want to distribute.  These can be backup files and snippets of code
that you want to keep for later.  Since the cvs command below imports all
files it can find, you need to have a directory tree with exactly those files
you want to store in CVS.  Best is to to make a copy of the project.  On Unix:
</para>
    
<literallayout>        cp -r projectdir tempdir
</literallayout>

<para>
Then delete all files you don't want to distribute.  Be especially careful
to delete "AAPDIR" directories and hidden files (starting with a dot).  It's
better to delete too much than too few: you can always add files later.
</para>
</listitem>


<listitem>
<para>
Import the project to the CVS repository
</para>

<para>
Move to the newly created directory ("tempdir" in the example above).
Import the whole tree into CVS with a single command.  Example:
</para>

<literallayout>        cd tempdir
        cvs -d:ext:myname@cvs.sf.net:/cvsroot/myproject import mymodule myproject start
</literallayout>

<para>
Careful: This will create at least one new directory "mymodule", which you
can't delete with CVS commands.
This will create the module "mymodule" and put all the files and
directories in it.  If there are any problems, read the documentation
available for your CVS server.
</para>
</listitem>

<listitem>
<para>
Checkout a copy from CVS and merge
</para>

<para>
Move to a directory where you want to get your project back.  Create the
directory "myproject" with this example: 
</para>

<literallayout>        cvs -d:ext:myname@cvs.sf.net:/cvsroot/myproject checkout mymodule
</literallayout>

<para>
You get back the files you imported in step 2, plus a bunch of "CVS"
directories.  These contain the administration for the cvs program.  Move
each of these directories back to your original project.  Example:
</para>

<literallayout>        mv myproject/CVS projectdir/CVS
        mv myproject/include/CVS projectdir/include/CVS
</literallayout>

<para>
    If you have many directories, one complicated command does them all:
</para>

<literallayout>        cd myproject
        find . -name CVS -exec mv {} ../projectdir/{} \;
</literallayout>

<para>
This is a bit tricky.  Another method is to copy all the files from your
original project into the newly created directory.  But then you have to be
careful not to change relevant file attributes, which is tricky as well.
Obviously, the best solution is to have all files you need in CVS, so that you
don't have to copy any files.
</para>
</listitem>

<listitem>
<para>
Commit changes
</para>

<para>
After making changes to your project and testing them, it's time to check
them in.  In the recipe you use for building, add a "commit" attribute to 
all the files that should be in CVS.  The
<link linkend="cmd-reviseall">:reviseall</link> command then does the
work for you (see above).  Example:
</para>

<programlisting>
        Files = $source $header main.aap
        :attr {commit = cvs://:ext:$CVSUSER_MYPROJECT@cvs.sf.net:/cvsroot/myproject} $Files
        :reviseall
</programlisting>

<para>
Careful: $Files must contain all files that you want to publish in this
directory and below.  If $Files has extra files they will be added in CVS.
Files missing from $Files will be removed from CVS.
</para>
<para>
You must assign $CVSUSER_MYPROJECT your user name on the CVS server.
Usually you do this in one of your personal A-A-P startup files, for
example "~/.aap/startup/main.aap".
</para>
</listitem>
</orderedlist>


<bridgehead>Using Sourceforge</bridgehead>

<para>
If you are making open source software and need to find a place to distribute
it, you might consider using SourceForge.  It's free and relatively stable.
They provide http access for your web pages, a CVS repository and a server for
downloading files.  There are news groups and maillists to support
communication.  Read more about it at http://sf.net.
</para>

<para>
Since you never know what happens with a free service, it's a good idea to
keep all your precious work on a local system and update the files on
SourceForge from there.  If several people are updating the SourceForge site,
either make sure everyone keeps copies, or make backup copies (at least
weekly).
</para>

<para>
You can use A-A-P recipes to upload your files to the SourceForge servers.  To
avoid having to type passwords each time, use an ssh client and put your
public keys in your home directory (for the web pages) or on your account
page (for the CVS server).  Read the SourceForge documentation for how to do
this.
</para>

<para>
For uploading web pages you can use a recipe like this:
</para>

<programlisting>
        Files = index.html
                download.html
                news.html
                images/logo.gif
        :attr {publish = rsync://myname@myproject.sf.net//home/groups/m/my/myproject/htdocs/%file%}
        $Files
</programlisting>

<para>
Start this recipe with the "publish" target.  If you don't have the "rsync"
command you might want to use "scp" instead.  The effect is the same, but
"rsync" works more efficient.
</para>

<para>
For sourceforge, set environment variable CVS_RSH to "ssh".  Otherwise you
won't be able to login.  Do "touch ~/.cvspass" to be able to use "cvs login"
Upload your ssh keys to your account to avoid having to type your password
each time.
</para>
