WorkShop Teamware: Solutions Guide: -

# Using the Configuring Tool File List Programs |

The default behavior of the Configuring tool bringover and putback transactions is to search the directories you specify for SCCS files and act on them. This default behavior (expanding specified directories into the SCCS files they contain) is the result of a script known as the default directory file list program, named `def.dir.flp`. This part of Configuring was purposely designed to be accessible to users so they could customize its behavior if the need arose.

The action of `def.dir.flp` can be changed by providing your own file list program (FLP), the function of which is to list the files you want to bring over or put back on `stdout`. The FLP is called by the Configuring transaction, which reads the list of file names and acts on them. Most FLPs are scripts, but any executable can be run as an FLP.

This topic describes how to use FLPs to full advantage.

## Levels of FLP Use

All Configuring users use FLPs, whether they know it or not. By running a bringover transaction and specifying directory names, for example a user causes `def.dir.flp` to run in each specified directory in both the parent and child workspaces. In the form in which `def.dir.flp` is shipped, it prints on `stdout` the names of all SCCS files in the directories and their subdirectories. The bringover transaction acts on the union of all listed files.

Because most projects take advantage of the hierarchical file system to organize their projects into directories, this default behavior is usually all that is required.

### Using Directory FLPs

The build process in some directories may require a file that is not under SCCS control or that is contained in the workspace but lies outside a specified directory. In these cases, users can place their own FLP, called `dir.flp`, in the directory. The `dir.flp` can simply echo the names of required files on `stdout`, or it can use an algorithm to produce the list of file names. For example, a directory FLP might parse the directory's makefile and list target dependencies.

The `def`.`dir`.`flp` looks in each specified directory for a `dir`.`flp`, and runs `dir`.`flp` if it exists. This is the most basic level of FLP customization.

### Using CODEMGR_DIR_FLP

You can also specify an FLP by setting the environment variable `CODEMGR_DIR_FLP` to the name of an FLP you want to use. The `def`.`dir`.`flp` checks to see if this variable is set, and if it is, it runs the FLP that is the variable's value. This is a more general level of customization because the FLP is used automatically by every transaction between the workspaces, regardless of directory.

### Specifying an FLP Directly

Another way to execute an FLP is to specify it directly on the command line or in the graphical user interface. When you specify an FLP in a bringover or putback transaction, the FLP runs once in the root directory of each workspace. Any directories you also specify are treated independently in the standard fashion, by running `def`.`dir`.`flp` in each of them. For example, consider the following command, executed in a child workspace:

    tutorial% bringover -f ~/aux.flp dir1 dir2

This command causes the FLP `aux`.`flp` (in this case, contained in the user's home directory) to be run in the root directories of the parent and child workspaces. The `aux`.`flp` can produce its list of file names in any fashion, but often it simply echoes a list of file names contained within the FLP itself. The `bringover` command treats the file names produced by `aux`.`flp` as though they had been specified on the command line along with `dir1` and `dir2`.

After `aux`.`flp` runs, the `bringover` command treats the specified directories in the standard fashion: it changes to each directory in turn in each workspace and runs `def`.`dir`.`flp`. The directories can be specified relative to the workspace roots, as are `dir1` and `dir2`, or specified as full path names (in which case Configuring computes the relative path names). If the directories lie outside of either workspace, they are ignored.

### Replacing the Default Directory FLP

The highest level of customization is to replace `def`.`dir`.`flp` with an executable of your own. When you customize `def`.`dir`.`flp`, your changes affect every bringover and putback at your site. This level of customization is powerful and imposes a corresponding amount of responsibility on the person who implements it. The customization itself, however, is quite easy to accomplish.

## FLP Execution Context

One limitation of FLPs arises from the fact that they are called automatically by Configuring. Because FLPs are not executed from the command line, you cannot pass values to them by means of command-line arguments. An easy workaround is to put values in environment variables that are then used by the FLP.

The last section mentioned the CODEMGR_DIR_FLP environment variable, which you can set to the name of an FLP to use with all bringover and putback commands. When a Configuring transaction starts, it sets several other environment variables for use by FLPs. These variables are summarized in Table 1. You can, of course, set other environment variables for use by your own custom FLPs.

#### Table 1 Environment Variables for Use with FLPs

Environment Variable | Meaning
`CODEMGR_CMD` | Contains the name of the Configuring transaction being run.
`CODEMGR_WS_CHILD` | Contains the name of the child workspace for this Configuring transaction.
`CODEMGR_WS_DEST` | Contains the name of the destination (to) workspace for this Configuring transaction.
`CODEMGR_WS_PARENT` | Contains the name of the parent workspace for this Configuring transaction.
`CODEMGR_WS_SRC` | Contains the name of the source (from) workspace for this Configuring transaction.
`CODEMGR_WS_ROOT` | The bringover and putback transactions execute FLPs in both the parent and child workspaces. This variable contains the name of the workspace in which the FLP is currently executing.

## The Default Directory FLP

Figure 30 shows the `def`.`dir`.`flp` that ships with every version of Configuring, omitting some environment-checking statements to improve clarity. The `def`.`dir`.`flp` checks for a setting for `CODEMGR_DIR_FLP`, then for the existence of a `dir`.`flp` in the current directory. The `def`.`dir`.`flp` runs these other FLPs, if they exist. Finally, it uses `find` to print all SCCS files in the current directory and its subdirectories to `stdout`.

    #! /bin/sh
    # Default directory file list program (FLP).
    # Decides which FLP to run.
    #
    # Look first for a shell environment variable named

    # CODEMGR_DIR_FLP. If it exists, assume it names

    # an FLP and run it.
    #
    # Next look for a file named "dir.flp" in the current
    # directory.  If it exists, run it.
    #

    # Finally, look for files under SCCS control.

    #
    #
    if [ -n "$CODEMGR_DIR_FLP" ]; then
    	$CODEMGR_DIR_FLP
    elif [ -f dir.flp ]; then
    	./dir.flp
    else
    	#
    	# Recursively find all the files in SCCS subdirectories

    	# below the current directory
    	#
    	find \Qpwd\Q -name 's.*' -print | grep '/SCCS/s\.'
    	#
    	# If find doesn't find anything it will exit with value 1
    	# and bringover and putback will stop.  Always exit 0 here.
    	#
    	exit 0
    fi

#### Figure 30 The Configuring Standard Default Directory FLP, `def.dir.flp`

Notice that the `find` command invoked in the `def`.`dir`.`flp` uses an expansion of ``pwd`` rather than `'.'` to represent the current working directory. The reason for this usage is that `find` may run in directories below the workspace root. The `'.'` notation represents each current working directory where `find` runs, but ``pwd`` produces a full path name to each of these directories. The full path name encompasses the workspace root, which Configuring can identify. Because each search must start at the workspace root, ``pwd`` gives Configuring a useful path name.

The `def`.`dir`.`flp` lists the full path names of the `s.*` files. The Configuring transactions use the full path names to compute path names relative to the workspace root. An FLP can also list the relative path names of files, in which case Configuring interprets them as relative to the workspace root. However, if a file name written by an FLP is not under either workspace named in the transaction, Configuring ignores it.

Figure 31 illustrates the flow control during a transaction as the `bringover` command executes FLPs. The diagram is somewhat simplified in that does not account for such features as checking the `Codemgr_wsdata/args` file in each workspace for recently used arguments.

    Click for closeup.

#### Figure 31 FLP Control Flow During a Bringover Transaction

## Directory File List Programs

A directory FLP (`dir`.`flp`) is typically used to bring over files that lie above the current working directory in the workspace directory tree.

Figure 32 shows a `dir`.`flp`, called by the standard `def`.`dir`.`flp`. It lists the name of a makefile, `Makefile`.`cmd`, in the bringover or putback transaction for the `doit` directory, where `dir`.`flp` is stored. `Makefile`.`cmd` is included in the local makefiles used in the builds for development of all of this project's commands.

#### Figure 32 A Directory FLP: `dir`.`flp (Continued)

    #! /bin/sh
    # Directory file list program (dir.flp).
    #
    # Report the master makefile required by

    # every makefile in the project

    #
    echo "$CODEMGR_WS_ROOT/usr/src/cmd/Makefile.cmd"
    #
    # Recursively find all the files in SCCS subdirectories

    # below the current directory
    #
    find \Qpwd\Q -name 's.*' -print | grep '/SCCS/s\.'
    #
    # If find doesn't find anything it will exit with value 1
    # and bringover and putback will stop.  Always exit 0 here.
    #
    exit 0

`

Note that control does not return to `def`.`dir`.`flp` after `dir`.`flp` exits. Therefore, a `find` statement identical to the one in the `def`.`dir`.`flp` has been included to search for source files under SCCS control.

Figure 33 shows the position of `Makefile`.`cmd` in the workspace directory hierarchy. When a bringover command specifies the `doit` directory, `Makefile`.`cmd` is brought over automatically.

    Click for closeup.

#### Figure 33 A `dir`.`flp` Brings a Makefile into a Development Workspace

Directory FLPs are useful for bringing special files into specific directories without having to list them manually during each bringover or putback. However, to achieve a global effect, you can modify the `def`.`dir`.`flp`.

## Customizing the Default Directory FLP

Some projects require that certain files (typically makefiles and header files) be brought over into every workspace where a build will take place. By customizing the `def`.`dir`.`flp`, you can globally specify the files to be moved in bringover and putback transactions. This section presents the experiences of two development groups who produced custom default directory FLPs. The examples illustrate different approaches to listing files not reported by the standard `def`.`dir`.`flp`.

### Finding Auxiliary Files with `def.dir.flp`

Development Group A needed to bring makefiles into each development workspace automatically. In their project structure, each build directory contains a local makefile that includes other makefiles from higher in the directory tree. All workspaces require one master makefile that is kept at the top of the workspace hierarchy, and each directory needs additional makefiles to completely describe local builds. The group decided not to write an individual FLP (`dir`.`flp`) for each directory. Instead, they maintain lists of the required makefiles in each directory and assign the task of echoing those lists to a custom `def`.`dir`.`flp`.

The custom `def`.`dir`.`flp` developed by Group A is shown in Figure 35. It is a C-shell script. In each of the project's development directories is a file, called `inc`.`fl`, that lists files that must be included in a bringover in order to build the executables in the directory. The file names can be absolute path names or names relative to the directory in which the `inc`.`fl` file resides. The `inc`.`fl` files are not directory FLPs or even scripts. They merely contain lists of files, although they may also contain comments preceded by "`#`" symbols.

Figure 35 adds two functions to the standard `def`.`dir`.`flp`:

- First, it searches the directory tree for `inc`.`fl` files and accumulates their absolute path names in a temporary file. The script then computes the absolute path names of the files listed in the `inc`.`fl` files and echoes them on `stdout`.

- Second, it searches for makefiles under SCCS control in the directory tree between the current working directory and the workspace root directory and echoes the names of these files on `stdout`.

An example of the effect of the custom `def`.`dir`.`flp` is shown in Figure 34. When a user brings over files into a workspace for building the `ls` command, the script finds two makefiles in the directory tree above the directory `ls` and brings them over into the workspace. It also finds an included file, `custom`.`h`, which was listed in `inc`.`fl`, and brings it over. In addition, the script brings over all the SCCS-controlled source files in and below the `ls` directory, just as the standard `def`.`dir`.`flp` does.

    Click for closeup.

#### Figure 34 A Custom `def`.`dir`.`flp` Brings Outlying Files into Each Development Directory

Note that Figure 35 provides another example of passing a value into an FLP with an environment variable. During the processing of the `inc`.`fl` files, the script replaces instances of the string "`$CODEMGR_WS_ROOT"` by the value of the environment variable of the same name. This technique can be applied to any environment variable.

#### Figure 35 A C-Shell Custom `def`.`dir`.`flp`

    #! /bin/csh -f
    #
    # This customized default directory FLP performs the following

    # functions:
    #
    # 1.			Does the same thing as the standard def.dir.flp, that is,

    #			looks for files under SCCS control in and below the
    #			current working directory.
    # 2.			Also reports the contents of any file named 'inc.fl'.

    #			Relative paths in 'd/inc.fl' are considered relative
    #			to directory 'd'.  Instances of the string

    #			"$CODEMGR_WS_ROOT" are replaced by the value
    #			of the environment variable of the same name.
    #			Comments preceded by '#' are ignored.
    # 3.			Also reports makefiles under SCCS in the directories

    #			between the current working directory an the workspace

    #			root directory.
    #
    #			Recursively find all the files in SCCS subdirectories

    #			below the current directory and accumulate path names of
    #			inc.fl files in a temp file.
    #
    set exit_status = 0
    cat /dev/null > /tmp/inc.fl$$
    find \Qpwd\Q -name 's.*' -print -o \
    	-name inc.fl -exec /bin/sh -c 'echo $0 >> \

    	'/tmp/inc.fl$$ {} \; | grep '/SCCS/s\.'
    #
    # For each inc.fl file listed in /tmp/inc.fl$$, write it to

    # stdout, processing the output as follows:
    #	1.		Delete lines which contain only a comment. For example,
    #				# This is a comment line
    #	2.		Strip comments off the end of lines. For example,
    #				/ws/Makefile 							# Top level makefile
    #		3.	Replace instances of '$CODEMGR_WS_ROOT' with environment

    # 			variable $CODEMGR_WS_ROOT. For example,
    #				$CODEMGR_WS_ROOT/usr/src/uts/README
    #		4.	Fully qualify relative pathnames with respect to the

    #			files' containing directory. For example:
    #						README
    #				might be converted to:
    #						/ws/usr/src/project/README
    #
    if (\Qwc -l < /tmp/inc.fl$$\Q != 0) then
    		# If the CODEMGR_WS_ROOT env is not set, set the

    		# SHELL variable by the same name so that the sed
    		# command can be executed. The leading "/" prevents
    		# this script from considering it a relative pathname.
    	if ( ! $?CODEMGR_WS_ROOT ) then
    			set basename = \Qbasename $0\Q
    			/bin/sh -c \

    			"echo '$basename':' warning - CODEMGR_WS_ROOT not set'\

    				>&2"
    			set CODEMGR_WS_ROOT = '/CODEMGR_WS_ROOT'
    		endif
    		foreach f ( \Qcat /tmp/inc.fl$$\Q )
    			sed -e '/[	 ]*#/d' -e 's/#.*//' \
    					-e 's#^\$CODEMGR_WS_ROOT#'$CODEMGR_WS_ROOT'#' \
    					-e 's#^[^/]#'\Qdirname $f\Q'/&#' $f
    		end
    endif
    #
    # Clean up.
    #
    rm -f /tmp/inc.fl$$
    #
    # Report any makefiles under SCCS in the directory between the

    # current working directory and the workspace root directory.
    #
    unset done
    while ( ! $?done )
    	# If the working directory has a Codemgr_wsdata directory,

    	# then the working directory is the root of the workspace.
    	# Because the workspace root might be missed, check
    	# for the working directory '/'.  Even if the ws root dir
    	# is missed and some SCCS files that are not in the workspace
    	# are reported, it won't cause any problems because

    	# Code Manager ignores such files.
    	#
    	if ( -d Codemgr_wsdata || (\Qpwd\Q == '/') ) then
    			set done
    	else
    			cd ..
    			if ( -d SCCS ) then
    				# Use the same find as is used above

    				# to find SCCS-controlled makefiles.
    				find \Qpwd\Q/SCCS -name 's.*' -print | \

    						grep '/SCCS/s\.*akefile*'
    			endif
    	endif
    end
    #
    # Always exit 0 here so that Code Manager

    # won't conclude that the FLP failed.
    #
    exit (0)

### Finding Auxiliary Files With Secondary Scripts

The next example is taken from the experience of Development Group B. Like Group A, Group B also needed to list makefiles in the directory tree above each specified directory. In addition, they wanted to identify and transfer specific files below each specified directory that might not be under SCCS control. Group B also decided against devoting a `dir`.`flp` in each directory to this task. Instead, they wrote a set of auxiliary scripts that are called by their custom `def`.`dir`.`flp` to identify these files. This arrangement allowed them to preserve the flexibility of placing a `dir`.`flp` in a specific directory if circumstances required.

The FLP shown in Figure 37 finds included files by descending the directory tree and calling a secondary script named `inc`.`flp` in each directory. Each `inc`.`flp` echoes the names of files that must be included in its directory in order to build successfully. The `def`.`dir`.`flp` performs a similar search up the directory tree, searching for and executing scripts named `req`.`flp`. Each `req`.`flp` echoes the names of required files such as makefiles that may reside above the specified working directory.

Using this custom `def`.`dir`.`flp`, the developer needs to specify only one directory, such as `/usr/src/cmd/ls`, in his bringover transaction. The `def`.`dir`.`flp` looks in the directory and recursively lists its SCCS-controlled files just as the standard `def`.`dir`.`flp` does. In addition, it looks for scripts named `inc`.`flp` or `req`.`flp` in each directory. If the scripts are in the directory, they are executed. The custom `def`.`dir`.`flp` then searches for `req`.`flp` in ancestor directories and for `inc`.`flp` in descendant directories, executing the scripts when it finds them (Figure 36).

    Click for closeup.

#### Figure 36 The Custom `def.dir.flp` Searches For and Executes Secondary Scripts

The custom `def.dir.flp` adapts to secondary scripts written in any shell language. If the secondary FLP (`inc`.`flp`, `req`.`flp`, `dir`.`flp`, or a script defined by `CODEMGR_DIR_FLP`) is written in Bourne shell, it is in-lined directly to take advantage of the functions defined in the custom `def`.`dir`.`flp`. If the FLP is written in another shell language, it is executed by its native interpreter.

#### Figure 37 A Bourne-Shell Custom `def`.`dir`.`flp`

    #! /bin/sh
    #
    # Default directory file list program (FLP),

    # modified by Group B.
    #
    # Look first for a shell environment variable named
    CODEMGR_DIR_FLP.

    # If it is exists, assume it names an FLP and run it.
    #
    # Next look for a file named "dir.flp" in the current
    # directory.  If that exists, run it.
    #
    # Finally, do the standard thing - look for files under SCCS.
    #
    # find_files(pat,dirs...)
    #
    # pat 			= pattern to pass to find
    # dirs			= space-separated list of directories for find to
    visit
    find_files() {
       	pat=$1
       	shift
    	for dir in $*
    	do
    			if [ -d $CODEMGR_WS_ROOT/$dir ]; then
    				find $CODEMGR_WS_ROOT/$dir -name "$pat" -print | \

    				grep "/SCCS/s."
    			fi
    	done
    } # find_files()
    echo_file() {
    	#
    	# Check to make sure a file exists, if it does then
    	# echo it out.
    	#
    	if [ -f $CODEMGR_WS_ROOT/$1 ]; then
    			echo $CODEMGR_WS_ROOT/$1
    	fi
    } # echo_file()
    # exec_file(script)
    #
    # script =
    #	full path name to script to be executed
    #			or
    #	relative path name to script to be executed
    #		 (relative to root of workspace)
    #
    # exec_file will execute the script pointed to by 'script'.

    # It will do this by interpreting the #! notation at the top
    # of the file to determine what 'interpreter' to run

    # for the script.
    #
    # Note: If the ((script == /bin/sh) || (script == /usr/bin/sh))

    # then it is actually in-lined with the
    #        . ${script}
    # notation.  This gives the additional functionality
    # of being able to inherit functions defined by the def.dir.flp
    # script (this file).
    #
    exec_file() {
    	FILE=$1
    	if [ \Qexpr "$FILE" : '^/'\Q = "0" ]; then
    			#
    			# If not absolute path, then prepend $CODEMGR_WS_ROOT
    to
    			# path.
    			#
    			FILE=$CODEMGR_WS_ROOT/$FILE
    	fi
    	MAGIC=\Qhead -1 $FILE\Q
    	if [ \Qexpr "$MAGIC" : '^#!'\Q = "0" ]; then
    			#
    			# No #! notation - assume this is a SHELL script
    			#
    			SHELL="/bin/sh"
    	else
    			#
    			# Set SHELL to string after #!
    			#
    			SHELL=\Qecho $MAGIC | sed -e "s/^\#\![ \t]*//"\Q
    	fi
    	if [ "(" \Qexpr $SHELL : '^/bin/sh'\Q = "0" ")" -a \
    			"(" \Qexpr $SHELL : '^/usr/bin/sh'\Q = "0" ")" ]; then
    			#
    			# Not Bourne shell, so execute the interpreter itself
    			#
    			$SHELL $FILE
    	else
    			#
    			# This is a Bourne script, so just in-line it.  This
    lets
    			# us take advantage of the find_file routine already
    in
    			# this script.
    			#
    			. $FILE
    	fi
    	#
    	# If a script was executed, echo it so it is transferred.
    	#
    	echo $FILE
    } # exec_file
    dodir() {
    	cd $1
    	for i in * .*
    	do
    	case $i in
    	\* | \. | \.\.)
    			;;
    	inc.flp)
    			exec_file $1/$i
    			;;
    	SCCS)
    			sccs_list=\Qecho $i/s.*\Q
    			if [ -d $i -a ! -h $i -a "$sccs_list" != "$i/"'s.*' ]
    ;\

    			then
    			for j in $i/s.*
    			do
    				echo $1/$j
    			done
    			fi
    			;;
    	*)
    			if [ -d $i -a ! -h $i ] ; then
    				dodir $1/$i
    				cd $1
    			fi
    			;;
    	esac
    	done
    } #dodir
    if [ -n "$CODEMGR_DIR_FLP" ]; then
    	$CODEMGR_DIR_FLP
    elif [ -f dir.flp ]; then
    	./dir.flp
    else
    	PWD=\Qpwd\Q
    	#
    	# The following couple of lines are to translate a
    	# CODEMGR_WS_ROOT path from a /net mount to a local
    	# directory if it is actually a local disk mount.
    	#
    	cd $CODEMGR_WS_ROOT
    	CODEMGR_WS_ROOT=\Q/usr/bin/pwd\Q
    	cd $PWD
    	#
    	# Find and execute all req.flp's above current directory.
    	#
    	lcd=$PWD
    	while [  \Qexpr $lcd : \.\*$CODEMGR_WS_ROOT\$ \Q = "0" ]
    	do
    			if [ -f $lcd/req.flp ]; then
    			 	exec_file $lcd/req.flp
    			fi
    			lcd=\Qdirname $lcd\Q
    	done #while
    	if [ -f $lcd/req.flp ]; then
    			exec_file $lcd/req.flp
    	fi
    	dodir $PWD
    	#
    	# If find doesn't find anything it will exit with value
    	# 1 and bringover and putback will stop.
    	# Always exit 0 here.
    	#
    	exit 0
    fi

For a view of this custom `def`.`dir`.`flp` in a project setting, see the description of the custom FLP in the TW Topic Chapter , "Managing SunOS Development With TeamWare" on page 34.

The custom FLPs shown in these examples are static descriptions of the dependencies of build targets. They or their secondary files (`inc.fl` in Figure 35, `inc.flp` and `req.flp` in Figure 37) must be edited when the directory structure or build target dependencies change. Nonetheless, they go a long way toward relieving the individual developer of annoying details when auxiliary files must be included in a workspace to enable a build.

## When to Use Custom FLPs

Custom FLPs are convenient and simple to use. They are useful when users must remember to transfer special workspace files that are kept outside a specified directory (such as header files, makefiles, and libraries) into a workspace. Because FLPs are executables, they are not limited to statically echoing of a list of file names. For example, an FLP could parse a makefile to identify source files that must be brought into a child workspace for a build.

The versatility of FLPs might tempt a user to use them for tasks for which they are not entirely suited. For example, some builds require the use of shared libraries or standard header files that are not kept in Configuring workspaces. Because workspaces are directories, a user can establish soft links to these files from his development workspace. Or, for performance reasons, he may want to copy the files directly into the workspace. In either event, FLPs should not be used to accomplish these tasks.

Soft links are best described by makefile rules so that `make` can establish them at the beginning of a build. The `make` command can guarantee that linked binaries are up to date, and describing the links in makefiles produces a build structure that works without the presence of Configuring.

FLPs are also not well suited to copying files directly into a workspace when the files are outside of either workspace named in a bringover or putback transaction. Copying such files is best accomplished by wrapping the `bringover` and `putback`

commands in executable shell scripts.1 The wrappers can copy the necessary files before they execute the bringover or putback. The advantage of separating the copying activity from the default FLP is that `def`.`dir`.`flp` may be called several times during a bringover or putback, causing wasteful overwrites of copied files.

1 The wrapping technique, which substitutes a shell script for the `bringover` and `putback` commands, works smoothly with the graphical user interface (GUI) because the GUI issues these commands in response to user selections in the GUI.