Previous Next Contents Index Doc Set Home


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:

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.


Previous Next Contents Index Doc Set Home