How To "Package" a Sun/Mesa Application
Mark Weiser, 11/27/88
A. Demers, September 8, 1989 6:28:36 pm PDT

In Sun/Mimosa/PCR language, "packaging" refers to building a standalone
Unix-style executable file, which starts up immediately without any loading
of other files.
Thus, a package corresponds roughly to a boot file in the Princops world.
This note describes how to package Sun/Mimosa applications.
You should already be familiar with Unix, C, Mimosa, Cedar, and the 'pcr'
Sun/Mesa/Cedar world.

Packaging a mimosa application is relatively easy,
once it works using dynamic loading.
Instead of dynamically loading the various packages needed by your application,
you bind them statically using the Unix linking program 'ld'.
Because this provides no opportunity to start the applications,
you must write a small amount of C code (one or two lines of code per package)
to start the each package in the order it is ordinarly loaded.
(If you bind your application into a single config, only one line is necessary).  If some of your packages are normally loaded directly by pcr and some by the command tool,
and your applications uses the command tool,
then you must start the CommandTool last.

So, let's suppose you have an application, called FOO,
and you have a command file MyCmdFile such that if you first say:

	setenv BasicDotLoadeeCmds MyCmdFile

and then start pcr, then everything runs the way you would like.
The packaged version of FOO will act just like pcr with MyCmdFile,
but will start much faster (within a few seconds, normally, if your machine
has enough real memory to hold it).

Now let's go through the steps to package FOO.
These steps are all driven by the contents of MyCmdFile,
so lets suppose MyCmdFile contains the following lines:

	unixload /pcedar2.0/cedarprebasics/sun4/cedarprebasics
	lr /pcedar2.0/cedarprebasicsextras/sun4/cedarprebasicsextras
	lr /pcedar2.0/cedarcore/sun4/cedarcore.c2c.o CedarCore
	lr /pcedar2.0/basiccedar/sun4/basiccedar.c2c.o BasicCedar
	lr /pcedar2.0/bulletproofvest/sun4/bulletproofvestimpl.c2c.o BulletproofVestImpl
	lr BAR.c2c.o
	lr BAZ.c2c.o
	lr FOO.c2c.o

The first five lines are the usual things to start up the Cedar world
(modulo the locations of CedarPreBasics, CedarPreBasicsExtras, CedarCore,
BasicCedar and BulletProofVestImpl at your installation).  The next two
lines load and start applications that FOO needs, and the final line
loads and starts FOO itself.

SPECIALLY WRITTEN C CODE:

To create a packaged version of FOO, you need to write two short C
programs, FooPackage.c and PackageVersion.c: 

----------------------------------------------------
/* FooPackage.c */

int XR_gcIgnoreDataAndBSS = 0;				/* NOTE (1) */

XR_init_Package()					/* NOTE (2) */
{
    if( XR_set_default_arg("-n", 0) != 0 )		/* NOTE (3) */
	return(-1);
    return(0);
}

#define Load(x) {XR_install_/**/x/**/();}
#define Run(x) {XR_run_/**/x/**/();}
#define LoadAndRun(x) {Load(x); Run(x)}			/* NOTE (4) */

XR_exec_Package()					/* NOTE (5) */
{
    Run(CedarPreBasics);
    LoadAndRun(CedarPreBasicsExtras);
    LoadAndRun(CedarCore);
    LoadAndRun(BasicCedar);
    LoadAndRun(BulletProofVestImpl);

    LoadAndRun(BAR);
    LoadAndRun(BAZ);
    LoadAndRun(FOO);

    XR_longjmp(XR_GetTopLevelJumpBuf(), 1);		/* NOTE (6) */
}
-------------------------------------------
/* PackageVersion.c */

char XR_packageVersion
	= "September 8, 1989 6:17:32 pm PDT";		/* NOTE (7) */

-------------------------------------------

NOTES:

(1) This line tells the garbage collector that the application is packaged.
(The collector needs to know this for technical reasons involving how it
traces the root set.)  If you forget this line, you'll get `multiply-defined'
messages from the loader about XR_exec_Package, etc.

(2)  This procedure really is named "XR_init_Package", NOT "XR_init_Foo".
Its purpose is to set default command line arguments (see note (3)).  It should
return (0) on success, or (-1) on failure.

(3)  Procedure XR_set_default_arg is called to set a default command line
argument.  In this (usual) case, we set a default of "-n" to inhibit
processing of any "BasicDotLoadeeCommands" file.
The procedure takes two parameters, because some command line arguments are
in two parts.  For example,
	XR_init_arg("-h", "100000") 
could be used to set a default initial heap size.  XR_init_arg returns (0) on
success, and (-1) on failure.

(4)  Load(x) corresponds to dynamically loading (and installing) module `x';
Run(x) corresponds to starting it. 

(5)  The procedure really is named "XR_exec_Package", NOT "XR_exec_Foo".
It contains a procedure call for each line of MyCmdFile; in particular, the last call
installs and runs Foo. 

(6)  When LoadAndRun(Foo) returns, the default is to return to the top-level
pcr interpreter.  The magic line:
	XR_longjmp(XR_GetTopLevelJumpBuf(), 1);
causes pcr to terminate instead.

(7)  The initialized string variable XR_packageVersion MUST be present
in your package; otherwise you'll get `multiply-defined' messages from
the loader about XR_packageVersion, etc.  Note the name of this variable
is "XR_packageVersion", NOT "XR_fooVersion".  It is strongly
suggested that you set up your makefile to generate PackageVersion.c
automatically.  An example of how to do this appears in makefile (and
../makefile.common) in this directory.

MAKING THE PACKAGE

Suppose your makefile can make the targetsBAR.c2c.o, BAZ.c2c.o, FOO.c2c.o
and PackageVersion.o.  To make a packaged version of Foo requires the
following additions to your makefile:

    XR_HOME=/jaune/xrhome
    XR_VERSION=INSTALLED
    XR_CONFIG=Threads
    XR_MACH=sparc
    XR_LIB_DIR=${XR_HOME}/${XR_VERSION}/LIB/${XR_CONFIG}-${XR_MACH}

NOTE: for `/jaune/xrhome', `INSTALLED', `Threads', and `sparc' make the
substitutions appropriate for your installation and application.

    FooPackage.o: FooPackage.c
		
    FooPackage:  FooPackage.o PackageVersion.o ...
		ld -Bstatic -dc -dp -e start -X -o FooPackage \
			${XR_LIB_DIR}/StartSegments.o \
			/usr/lib/crt0.o \
			-Bstatic \
			FooPackage.o PackageVersion.o \
			${XR_LIB_DIR}/XRRoot.o ${XR_LIB_DIR}/xr.a \
			-lc \
			${XR_LIB_DIR}/StartSharedSegments.o \
			/pcedar2.0/cedarprebasics/sun4/cedarprebasics \
			/pcedar2.0/cedarprebasicsextras/sun4/cedarprebasicsextras \
			/pcedar2.0/cedarcore/sun4/cedarcore.c2c.o \
			/pcedar2.0/basiccedar/sun4/basiccedar.c2c.o \
			/pcedar2.0/bulletproofvest/sun4/bulletproofvestimpl.c2c.o \
			BAR.c2c.o BAZ.c2c.o FOO.c2c.o \
			${XR_LIB_DIR}/libxrc.a

The command "make FooPackage" will create a packaged Foo which, when run,
would behave like pcr after running the command file MyCmdFile.

This complicated `ld' command was invented by suitably modifying the output
of a `cc -dryrun' command to get all the standard C libraries and setup correct.


SPECIAL CASES

SPECIAL 1. What happens if some of your packages are loaded from the command
tool?  Well, suppose that BAR, BAZ, and FOO above are actually loaded by the
command tool and not in MyCmdFile at all, and suppose that MyCmdFile looked
like this:

	unixload /pcedar2.0/cedarprebasics/sun4/cedarprebasics
	LoadAndRun /pcedar2.0/cedarprebasicsextras/sun4/cedarprebasicsextras
	LoadAndRun /pcedar2.0/cedarcore/sun4/cedarcore.c2c.o CedarCore
	LoadAndRun /pcedar2.0/basiccedar/sun4/basiccedar.c2c.o BasicCedar
	LoadAndRun /pcedar2.0/neededbycmdtool/sun4/neededbycmdtool.c2c.o NeededByCmdTool
	LoadAndRun /pcedar2.0/cmdtool/sun4/cmdtool.c2c.o CmdTool

Then everything goes as before in the makefile (you need to include in the
CC command that creates FooPackage everything loaded either in MyCmdFile or
later via the CommandTool), and FooPackage.c looks like this:

----------------------------------------------------
/* FooPackage.c */

int XR_gcIgnoreDataAndBSS = 0;				/* NOTE (1) */

XR_init_Package()					/* NOTE (2) */
{
    if( XR_set_default_arg("-n", 0) != 0 )		/* NOTE (3) */
	return(-1);
    return(0);
}

#define Load(x) {XR_install_/**/x/**/();}
#define Run(x) {XR_run_/**/x/**/();}
#define LoadAndRun(x) {Load(x); Run(x)}			/* NOTE (4) */

XR_exec_Package()					/* NOTE (5) */
{
    Run(CedarPreBasics);
    LoadAndRun(CedarPreBasicsExtras);
    LoadAndRun(CedarCore);
    LoadAndRun(BasicCedar);
    LoadAndRun(BulletProofVestImpl);

    LoadAndRun(NeededByCmdTool);
    Load(CmdTool);
    LoadAndRun(BAR);
    LoadAndRun(BAZ);
    LoadAndRun(FOO);
    Run(CmdTool);

    XR_longjmp(XR_GetTopLevelJumpBuf(), 1);
}
-------------------------------------------

The difference is, now you want to delay starting the command tool
until the very end, because once it starts it never returns and so
nothing else can get loaded or started.

SPECIAL 2. What if your application accepts command via the command tool,
and you want to hide this from the Unix user?  Easy: use a shell script.
For instance, the following Unix C-shell script is the one used for the packaged
mimosa compiler.  ('packmimosa' is the packaged mimosa program):
---------------------------------
#!/bin/csh -f
packmimosa -h 2100 << //sysindd//
mimosa $*
quit
//sysindd//
----------------------------------
The commands 'mimosa' and 'quit' are both CommandTool commands.  The $* is
expanded by the C-shell into the list of arguments given to the script itself.
The '-h 2100' forces packmimosa to start with a heap size of 2100 blocks
(mimosa needs a lot, and this helps it start faster.  It's optional).

SPECIAL 3.  There are two things that can go wrong with the 'ld' command which
builds the packaged application.  They are both indicated by error messages:
either "duplicate symbol" or "undefined symbol".

If you get "duplicate symbol" messages, it probably means that some of the
.c2c.o files being 'ld'd together were already linked with -lcedarc.  Try to find the original .o files, before linking with -lcedarc, and use those.

If you get 'undefined symbol' messages, it might be because you did not use the
right compiler switch to make visible the 'install' and 'run' routines in your
packages.  (The symbol that is undefined will be "XR_install_packagename" or
"XR_run_packagename", where packagename is one of the packages you are loading.)
You can either recompile the package with the right switch, or modify .c2c.o
file in place to make the symbol visible.  The latter is done as follows:

	FixPackage packagename.c2c.o modulename

FixPackage turns off the external bit for all other symbols; thus, using it may
eliminate "duplicate symbol" messages.

The source and executable versions of FixPackage are included in this directory.

EXAMPLES

The file NullPackage.c contains the source for the package "stub" that is
included in the xr.a library.  It gets included by default a non-packaged
versions of pcr.

Look at CedarPackage in this directory for an example of a simple but very
useful package.
