= Legacy applications =
A '''legacy application''' is one which doesn't use the BOINC API
(for example, because the source code is not available).
Such applications can be run under BOINC using a 'wrapper application' supplied by BOINC.
The wrapper handles all communication with the core client,
and runs the legacy application as a subprocess:
[[Image(http://boinc.berkeley.edu/wrapper.png)]]
The wrapper program (called '''wrapper''') is in [ExampleApps boinc_samples].
It reads a file with [BoincFiles logical name] 'job.xml'.
This file has the format:
{{{
worker_5.10_windows_intelx86.exe
[ stdin_file ]
[ stdout_file ]
[ stderr_file ]
[ --foo bar ]
[ ... ]
}}}
The job file specifies a sequence of tasks.
The descriptor for each task includes the name of the application, or 'worker program'.
If the worker program uses standard I/O (stdin, stdout or stderr)
the descriptor specifies the logical names of the files
to which these are to be connected.
The descriptor may also specify command-line argments to be passed to the worker program.
'''wrapper''' itself may be passed command-line arguments (specified in the workunit template);
these are passed to each of the worker programs after those specified in the job file.
The job file can specify multiple tasks.
This is useful for two purposes:
* To handle jobs that involve multiple steps (e.g., preprocessing and postprocessing).
* To break a long job up into smaller pieces. This provides a form of checkpointing: ''wrapper'' does checkpointing at the task level, so that lost CPU time is limited even if the legacy applications themselves are not restartable.
Notes:
* The job file can be part of the workunit (e.g. if its command line elements differ between workunits) or the application version (if it's the same between workunits).
* Files opened directly by a worker program must have the tag. This requires version 5.5 or higher of the BOINC core client (you can specify this limit at either the [AppVersion application] or [ProjectOptions project] level.
* If you run wrapper in standalone mode (while debugging), you must provide input files with the proper logical, not physical, names.
== Example ==
Here's an example that shows how to use this mechanism.
We assume that you have already [MakeProject created a project] with root directory PROJECT/.
* Compile the program 'worker' from the [ExampleApps boinc_samples] tree, producing (say) 'worker_5.10_windows_intelx86.exe'. This is the legacy app. If reads from stdin and writes to stdout; it also opens and reads a file 'in', and opens and writes a file 'out'. It takes one command-line argument: the number of CPU seconds to use.
* Compile the program 'wrapper' from the [ExampleApps boinc_samples] tree, producing (say) 'wrapper_5.10_windows_intelx86.exe'. This program executes your legacy application, and acts as a proxy for it (to report CPU time etc.).
* [AppVersion Create an application] named 'worker' and a corresponding directory 'PROJECT/apps/worker'. In this directory, create a directory 'wrapper_5.10_windows_intelx86.exe'. Put the files 'wrapper_5.10_windows_intelx86.exe', and 'worker_5.10_windows_intelx86.exe' there.
* In the same directory, create a file 'job.xml=job_1.12.xml' (1.12 is a version number) containing
{{{
worker_5.10_windows_intelx86.exe
stdin
stdout
10
}}}
This file (which has logical name 'job.xml' and physical name 'job_1.12.xml') is read by 'wrapper'; it tells it the name of the legacy program, what files to connect to its stdin/stdout, and a command line.
* In the 'PROJECT/templates' directory create a workunit template file called 'worker_wu':
{{{
0
1
0
in
1
stdin
1000000000000
1000000000000
}}}
and a result template file called 'worker_result'
{{{
5000000
5000000
out
stdout
}}}
* Run [UpdateVersions bin/update_versions] to create an app version and to copy the application files to the 'PROJECT/download' directory.
* Run [StartTool 'bin/start'] to start the daemons.
* Run a script like
{{{
#! /bin/sh
cp download/input `bin/dir_hier_path input`
cp download/input2 `bin/dir_hier_path input2`
bin/create_work -appname worker -wu_name worker_nodelete \
-wu_template templates/worker_wu \
-result_template templates/worker_result \
input input2
}}}
to generate a workunit. The order of the input files in the 'create_work' command has to be the same as in the workunit template file (worker_wu). Otherwise the client will generate errors when processing the workunit.
To understand how all this works: at the beginning of execution, the file layout is:
||'''Project directory'''||'''slot directory'''||
||input||in (copy of project/input)||
||job_1.12.xml||job.xml (link to project/job_1.12.xml)||
||input2||stdin (link to project/input2)||
||worker_nodelete_0||stdout (link to project/worker_nodelete_0)||
||worker_5.10_windows_intelx86.exe||worker_5.10_windows_intelx86.exe (link to project/worker_5.10_windows_intelx86.exe)
||wrapper_5.10_windows_intelx86.exe||wrapper_5.10_windows_intelx86.exe (link to project/wrapper_5.10_windows_intelx86.exe) ||
The wrapper program executes the worker, connecting its stdin to project/input2 and its stdout to project/worker_nodelete_0. The worker program opens 'in' for reading and 'out' for writing.
When the worker program finishes, the wrapper sees this and exits. Then the BOINC core client copies slot/out to project/worker_nodelete_1.