Provided by: makepp_2.0.98.5-2.1_all bug

NAME

       makepp_repositories -- How to use repositories for variant builds, for maintaining a
       central set of sources, and other things

DESCRIPTION

       A repository is a directory or directory hierarchy outside of the default directory that
       contains files which the makefile needs in the current directory tree.  Makepp can
       automatically link files from the repository into the current directory tree if they are
       needed.  Repositories provide similar functionality to the "VPATH" variable, but (unlike
       "VPATH" in other versions of make) you do not have to do anything special to your makefile
       to get them to work.

       Repositories are specified with the "-R" or "--repository" command line option or with the
       "repository" statement in the makefile.  Note that if you have a habit of calling makepp
       in different subdirectories of your build tree, it is easy to accidentally reimport a
       repository somewhere else.  As a safeguard against this, if you use RootMakeppfile, makepp
       will refuse to start if it finds one above or below where it would be imported.

       This is somewhat comparable to operating system union filesystems (unionfs...)  The
       current directory is like the highest level writable layer.  All repositories are like
       lower read-only layers.

       Repositories are useful in several different situations:

       •   When you want to place your object and executable files in a separate directory, but
           the makefile is written to place them in the same directory as the sources.

       •   When you want to build the same program two different ways (e.g., with two different
           sets of compilation options, or for two different architectures).

       •   When you don't have write access to all or part of the source tree.

       •   When several developers are working on the same project, and there is a common source
           repository containing all the sources for the project.  Each developer can modify only
           the files he needs to change in his local directory without affecting the other
           developers, and makepp will automatically fetch the unmodified files from the source
           repository.

       Makepp's implementation of repositories does not require rewriting of the build commands
       at all, unlike (for example) repositories in cons.  Makepp puts a symbolic link into the
       directory where the command is expecting it.  As long as the command does not refer to
       absolute directories, the exact same shell command will work with files from a repository.
       This means that it works not only for compilation commands, but any kind of command you
       can think to put in your makefile.

       Makepp has another kind of mechanism called a build cache which solves some of the same
       sorts of problems as repositories in a different way.  Depending on your problem, a build
       cache may be more useful than a repository.  See makepp_build_cache for information about
       build caches and a comparison of build caches and repositories.

   Examples
       Repositories are best explained by several examples of what you can do.

       Different compilation options

       Suppose you have a simple program with a makefile that looks something like this:

           CFLAGS      = -O2
           OBJECTS = a.o b.o c.o
           my_program: $(OBJECTS)
               cc $(inputs) -o $(output)

           %.o: %.c
               cc $(CFLAGS) -c $(input) -o $(output)

       This makefile places the files "a.o", "b.o", "c.o", and "my_program" in the same directory
       as the source files.

       Sometimes you want to place the binary files into a separate directory.  For example, you
       might build your program on several different architectures, and you don't want the binary
       files on one architecture to be replaced with the binary files on the other.  Or you might
       want to make a temporary change and recompile without wiping out the previous compilation
       results.  Without repositories, you would have to modify your makefile to place the
       objects elsewhere.

       With a repository, however, you don't have to touch your makefile at all.  Consider the
       following sequence of commands:

           % cd my_program_source
           % makepp                    # Builds using the above makefile, and
                                       # object files go into the directory
                                       # my_program_source.
           % cd ..
           % mkdir binary-debug        # Make a clean directory for building the
           % cd binary-debug           # same program with different options.
           % makepp -R ../my_program_source CFLAGS=-g
                                       # Now objects go into binary-debug.

       The first makepp command compiles the source files with optimization and puts the objects
       into the directory "my_program_source", because that's what the makefile is supposed to
       do.  Now we want to rebuild the program, but we want to change the value of "CFLAGS" to
       compile for debug.  We specify the new value of "CFLAGS" on the command line, and we also
       tell makepp that the "my_program_source" directory is a repository using the "-R" option.

       Every time makepp realizes that it needs a file that it doesn't already have in current
       directory, it looks in the repository.  In this case, it first looks for the makefile,
       which doesn't exist in the "binary-debug" subdirectory.  So it creates a symbolic link to
       it from the makefile in "my_program_source", and then reads in the makefile.  Then it
       notices that it needs the file "a.c" in order to build "a.o", and so it links in "a.c"
       from the repository.  If "a.c" includes any files contained in "my_program_source", then
       these will be automatically linked in as well.  Note: Those links are useful for things
       like debugging, but if you don't like them, "makeppclean -R" can remove them.

       Running the build command in "binary-debug" won't touch any of the files in
       "my_program_source".  Thus from the same set of source files, you now have two different
       copies of the program, one compiled with optimization and one compiled for debug.  And
       this happened without touching the makefile at all.

       The advantage of using repositories instead of simply recompiling and overwriting the
       original binaries is that now if we fix our bugs and want to go back to the optimized
       version, we don't have to recompile everything.  Since the original object files are still
       around, and most of them are still valid, we can save a lot of time on recompilation.
       This does not make a big difference when only three source files are involved, but for a
       larger build that takes minutes or hours to complete, the savings in programmer time and
       frustration can be significant.

       Rebuilding one file with a minor modification to the compilation commands

       Makepp doesn't fetch only source files from the repository.  If the object files in the
       repository don't need rebuilding, it will use them.  For example, consider a slight
       modification to the above makefile:

           CFLAGS   := -O2
           A_CFLAGS := -O6 -funroll-loops

           OBJECTS := a.o b.o c.o

           my_program: $(OBJECTS)
               cc $(inputs) -o $(output)

           %.o: %.c
               cc $(CFLAGS) -c $(input) -o $(output)

           a.o: a.c
               cc $(A_CFLAGS) -c $(input) -o $(output)

       The idea is that "a.o" contains the time-critical code, so it is compiled with higher
       optimization than the rest of the objects.  Now suppose we want to test just how different
       the timing is with different compile options.  A repository can help with this, too:

           % cd my_program_source
           % makepp                    # Builds using the above makefile, and
                                       # object files go into the directory
                                       # my_program_source.
           % cd ..
           % mkdir no-unrolling        # Make a clean directory for building the
           % cd no-unrolling           # same program with different options.
           % makepp -R ../my_program_source A_CFLAGS=-O2
           % cd ..
           % time no-unrolling/my_program # Benchmark the two versions of the program.
           % time my_program_source/my_program

       Makepp proceeds as before, linking in a copy of the makefile and then examining the object
       files.  Now only the "a.o" module needs recompiling, since the options for "b.o" and "c.o"
       haven't changed.  Makepp notices that it can use "b.o" and "c.o" from the repository, so
       it just links those in.  However, it will recompile "a.o" in the "no-unrolling" directory.
       Once the compilation is finished, the two different versions of the program can be
       benchmarked.

       Rebuilding with a minor modification to the source

       Now suppose we want to make a change to "a.c" and benchmark the program before and after
       the change.  Repositories can help again.  Consider this sequence of commands:

           % mkdir modified-a
           % cp my_program_source/a.c modified-a
           % cd modified-a
           % emacs a.c                 # Make some modifications just to this module.
           % makepp -R ../my_program_source

       Here we have created a new directory that just contains the single source file we want to
       modify.  Makepp now takes "a.c" from the "modified-a" subdirectory, but uses the copies of
       "b" and "c" from the "my_program_source" directory.  Without changing any of the binary
       files in "my_program_source", we have created a separate copy of the program that
       incorporates our changes to "a.c".  If there are other developers using the sources in
       "my_program_source", they will be unaffected by our changes.

       Repositories can thus be used as a quick way to build variants of a program, without
       adding complicated conditions to the makefile.  None of the files in the original
       directory are modified; they are used as needed.

       Using a directory hierarchy

       A repository is actually not just a single directory, it's a whole directory hierarchy.
       Suppose you use /our/library as a repository.  Now /our/library may well contain many
       subdirectories, e.g., /our/library/gui and /our/library/network.  Consider this command:

           % makepp -R /our/library

       Any commands in the makefile that refer to files in the directory ./network will actually
       get files from /our/library/network, and similarly for ./gui.  Makepp automatically
       creates any directories that exist in the repository but not in the current directory.

       Linking to any place in the file system

       All of the above examples show files from a repository being linked into the current
       directory or its subdirectories, but you can actually have makepp link them into any place
       in the file system that you have write access to.  This is done by specifying
       "-R new-location=old-location".

       For example, sometimes it's a little tedious to type the following:

              mkdir alternate-build
              cd alternate-build
              makepp -R ..

       You can do it all with one command, like this:

              makepp -R alternate-build=. -F alternate-build

       "-F" or "-makeppfile" changes to that directory before loading the makefile.  You must
       specify "-R" before "-F".  Note that this example puts the new build tree inside the
       repository.  That will not work if you use a RootMakeppfile because makepp safeguards
       against nested trees.  It's also not a good idea if you use **, because if you ever build
       in the repository it will also find edited and generated files in this subtree.

       Assigning a different location in the file system may be also useful for more complicated
       builds, where there are several library subdirectories.  For example, here's a command I
       have used to build variants of one of my programs:

           % makepp -R test-build/seescape=/src/seescape \
                -R test-build/HLib=/src/HLib \
                -R test-build/H5pp=/src/H5pp \
                -R qwt=/src/external_libraries/qwt \
                -F test-build/seescape

       This command loads in files from four different repositories, and then cds to the
       ./test-build/seescape directory and executes the makefile there.  Files contained in the
       directory tree beginning with /src/seescape are linked into ./test-build/seescape.  In
       other words, makepp will temporarily link the file /src/seescape/gui/image_canvas.cxx to
       ./test-build/seescape/gui/image_canvas.cxx when it is needed.  This command will work even
       if the "test-build" directory doesn't exist yet; makepp will create it for you.  (But you
       must specify the "-R" options before the "-F" option on the command line.)

       Multiple equivalent repositories

       Say your project is maintained by several fairly autonomous groups.  You could have one
       complete repository with all the sources as they are in production or at least
       successfully tested.  Every group can have a mostly empty repository with (part of) the
       same structure, containing the files group members have finished developing.

       Developers' current directories will have the files they are still working on.  The group
       repository will be the first one given and the production repository the last one, so that
       it furnishes the files not found in the group repository:

           $ makepp -R/path/to/group/repository -R/path/to/production/repository

       Since this is probably fairly static for that directory, you may want to put a file
       .makepprc at its root with the following content:

           -R/path/to/group/repository -R/path/to/production/repository

       Or, presuming that it has a fixed path, you could write into your makefile:

           repository /path/to/production/repository

       and, because options are seen before makefiles are read, you can then call just

           $ makepp -R/path/to/group/repository

       Repositories as fixed part of your build system

       If you know you always use some repository you can use the "repository" or "vpath"
       statements in your makefile.

   Caveats with repositories
       When the links get in the way

       For finding your way around your file hierarchy and for allowing the debugger to find the
       sources it is useful to have the links used while building.  But when you want to edit a
       file or resync it with your version control, the links can get in the way.  That is
       because the system traverses the link and writes to the file in the repository.  Unless
       it's your personal repository used just for keeping things apart, that may not be what you
       want.

       As a safeguard against inadvertent overwriting of public files it is suggested to make the
       sources in the repository unwritable.  It might even not be enough to remove the write
       bit, because a version control system which insists on your locking the files for editing
       might also do that, but temporarily make the file writable while resyncing it.  If that is
       the case for you, the repository should actually belong to a different user.

       There are a few tactics to surmount this:

       •   Keep the sources you edit in a repository, separate from your build tree.  Whenever
           you put a file, which was previously fetched from another repository, into this
           editing repository, makepp will notice and fetch it from there, provided it is the
           first repository you specify.

       •   Remember to delete any file, before you create a copy for writing.  If you follow the
           safeguard suggestion above, forgetting to do this will give an error message when
           writing.  To help you, the following function "delink" will replace one link by a copy
           of the linked file.  The first variant is for all kinds of Bournish Shells, the second
           one for csh (or at least tcsh):

               $ delink() { { rm $1 && cat >$1; } <$1; }
               % alias delink '( rm \!:1 && cat >\!:1; ) <\!:1'

       •   If you feel you don't need them, you can delete them all, whenever you want, e.g.
           after every makepp run, possibly backgrounded (either short or long form):

               makeppclean --recurse --only-repository-links
               mppc -rR

       Don't build in a repository during use

       A repository is meant to be read-only while it is being used as a repository.  Makepp will
       not work properly if you change files in your repository during the course of a build.
       Nightly builds may be ok for you, if no one else uses the repository at that time.  Before
       it starts the build, makepp gets a list of all the files that exist in the repository, and
       never updates its list, except for files it expects to appear.

       If you need a repository that's changing as you build, you might want to consider makepp's
       build cache mechanism (see makepp_build_cache).  Alternatively, you can use a "poor man's
       repository": you can put explicit rules into your makefile to create the soft links, like
       this:

           %.c : $(directory_I_wish_was_a_repository)/%.c
               &ln -fs $(input) $(output)

       This works only for source files; you can't easily use this to link a file if it is
       already built in the repository, but build it here if it's not already built, since there
       is only allowed to be one way to build a file.

       Use only relative filenames

       Repositories work completely transparently if the makefiles use only relative filenames.
       In the above example, it's ok if the makefile in /src/seescape refers to ../HLib, but the
       above command will not work as expected if it refers to /src/HLib.  If you need to use
       absolute file names, you can put them into make variables and then override them on the
       command line, like this:

           % makepp -R test-build/seescape=/src/seescape SEESCAPE=/home/holt/test-build/seescape \
                -R test-build/HLib=/src/HLib HLIB=/home/holt/test-build/HLib \
                -R test-build/H5pp=/src/H5pp H5pp=/home/holt/test-build/H5pp \
                -R qwt=/src/external_libraries/qwt QWT=/home/holt/test-build/qwt \
                -F test-build/seescape

       The above will work as long as the "HLib" directory is referred to as "$(HLIB)" in all the
       makefiles.  Note that you have to specify absolute paths for the directories, because
       makepp cd's to "test-build/seescape" before reading the makefile.  This leads to long and
       complicated make commands; use relative paths when possible.

       Makepp must know about all dependencies

       Repositories will not work if there are hidden dependencies that makepp doesn't know
       about.  (In fact, doing a build using repositories, is one way of checking for forgotten
       dependencies.  But, just for this check, don't combine it with a build cache, since
       fetching something there, instead of building it, might hide a forgotten dependency.)
       Sometimes these dependencies can be fairly subtle.  For example, the libtool command will
       not only create ".lo" and ".la" files as listed on the command line, but it also may
       create a subdirectory called ".libs" which contains the actual object files.  To prevent
       build mistakes, makepp refuses to link in a ".la" file from a repository.  Hopefully in
       the future libtool will be better supported.

       Many hidden dependencies related to compilation are caught by the command line scanner.
       If your compiler uses the common Unix compilation flags (e.g., "-I", "-D", etc.), then
       makepp will usually figure out where all your include files are.  You may have to be
       careful if you have any homegrown scripts that create files that makepp doesn't know
       about.  For correct builds, it is vitally important to list all targets and dependencies
       (or determine them automatically by scanning).

       Putting absolute filenames into programs

       Repositories will also not work if any of the files built contain absolute file names in
       them (e.g., if any of your build commands write out an absolute filename).  For example,
       it turns out that the ".la" files produced by libtool have this property.  (If you look at
       the contents of the ".la" file you'll see that the dependency list contains absolute
       filenames.)  In order to solve this particular problem, makepp will not link ".la" files
       from a repository; it will insist on rebuilding them.

       Avoid linking in unnecessary directories

       Repositories can be slow on startup and use a lot of memory if there are a lot of
       unnecessary files in the repository.  For example, if you use an automatic HTML
       documentation generator which makes thousands of ".html" files from your source code, you
       may not want to put them in a subdirectory of a directory that's used as a repository.
       It's better to put them in a different directory tree entirely, so the repository
       mechanism won't load in their names.

       Too Many Files

       The disadvantage of repositories is that symbolic links, which the repository mechanism
       uses, are individual files (though they use almost no disk space).  This is unlike real
       links, but those can't cross file system boundaries.  In extreme cases the presence of
       very many symbolic links can lead to exhaustion of the number of foreseen files (so called
       inodes), even though there is plenty of space left.  In this case the sysadmin will need
       to tune the file system.

   Overriding repository copies
       If you make any modifications to a file locally, makepp will ordinarily realize this and
       recompile the file using the local copy rather than the repository copy.

       If you're using a repository to maintain a central code base, and you have developers
       working on local copies which contain only the files they have modified, one problem that
       comes up is: what if a developer wants to remove a file from his local build but the
       repository still contains it?  If the developer removes the local copy, makepp will
       happily put in the copy from the repository, and the build will proceed as if the file
       existed.

       One technique (alas not for user root) for this problem is to make the file that you want
       not to include in the build process unreadable, like this:

           chmod a-rw file-to-be-excluded

       This will prevent makepp from incorporating it from the repository.  Makepp also includes
       special code so that unreadable files do not match wildcards or pattern rules.

       Similarly, to prevent makepp from incorporating an entire subdirectory, make a local
       directory that has the same name but is unwritable.  If you want makepp to ignore the
       directory entirely, then make it unreadable too.  (Read-only directories are searched but
       targets in them are usually not built.)

       The other way to do this is calling makepp with one or more exclusion options:

           mpp -R /path/to/rep --dont-read=/path/to/rep/file-to-be-excluded

   Don't use repositories for files which can change!
       Don't try to use a repository for a file which is part of your build.  For example, you
       might be tempted to try to use repositories to put all of your public .h files in the same
       directory, like this:

           # top level makefile
           repository include=module1/include
           repository include=module2/include
           repository include=module3/include
           repository include=module4/include

       This is probably not a good idea if any of the .h files are themselves outputs of a
       program (e.g., yacc or some other program that spits out C source code), because makepp
       assumes that files in repositories never change.  If the build needs include/xyz.h, and
       module2/include/xyz.h actually needs to be produced by some program, makepp will not know
       to run the program.  It's better to use a technique like this to put all of your .h files
       into a common include directory:

           # module1/Makeppfile
           ../include/%.h : include/%.h
               &cp $(input) $(output)
           # You could also (more efficiently but problematic on Windows) do the following:
           #    &ln -r $(input) $(output)

       Makepp might still try to build files that happen to be in a repository if something asks
       for them directly, but it won't build them on behalf of the local directory.  The result
       of this can be quite confusing, because it can lead to a repository symbolic link being
       used while its repository target is out-of-date, but that target might get updated later
       in the build.  You can prevent this from happening either by making sure that the
       repository is referred to only through the repository path, or by making sure that there
       is also a local rule for all the generated repository files.

       Another way to avoid recompiling identical files in different directories is to use a
       build cache (see makepp_build_cache for details).  A build cache does not have the
       restriction that the file may not change.