= 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 arguments 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., pre-processing and post-processing). * 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#Clientcontrol 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.