wiki:FAQ_CompiledMatlab
Last modified 3 months ago Last modified on 03/27/14 16:48:27

How to compile MATLAB applications

Topics discussed:

  1. Why compile my MATLAB code?
  2. Files Involved In Compilation
  3. Compiling and Makefiles
  4. Installing
  5. Testing
  6. Example

Why compile my MATLAB code?

Due to restrictions in the MATLAB software license, users must compile their MATLAB code if they want to enable someone without a MATLAB license to run it. While the user's MATLAB code can be freely distributed to others, the MATLAB engine cannot.

As an alternative, there are plenty of other programming languages that provide powerful interfaces for working with matrices and other mathematical structures, and do not have the license restrictions and hassle associated with compiling MATLAB code. Some of the more popular tool kits include Octave and Python. Versions 3.2.4 and up of Octave provide users with a system very similar to that of MATLAB. Often times, you can take code written in MATLAB, and run it using the Octave system without problem. Most of the problems we have experienced with this approach have been solved by making simple fixes to the MATLAB code, like changing function names. Similarly, Python provides an interface for working with mathematical concepts though the SciPy and MatPlotLib libraries.

Both Octave and Python are active open source projects which do not unreasonably restrict the use and distribution of their software, or the software written using their software.

Files Involved In Compilation

  1. Makefile
    File containing instructions for compiling.
  1. main_fermi.m
    MATLAB source code that acts as a main function. This function sets up a variable named lib, and tries to open the xml file provided as the function's argument. Next it calls the function named fermi, with lib as an argument, where the science calculations are performed. If there is an error while opening the xml file or running the fermi function, an error message is written to a file named errorDetected.txt. If the Rappture library object was successfully created, the same message is written to the library object.
  1. fermi.m
    MATLAB source code with scientific calculations. The science code should be in a function so it can be called from the main function.

Compiling and Makefiles

As a part of good software development practices, all projects should have a Makefile. The purpose of the Makefile is to gather all of the commands needed to compile and install the source code. For the user of the code, compiling and installing should be as simple as the commands:

make clean
make all
make install

On most unix-like platforms, the make command is available and by default looks for a file named Makefile in the current working directory. The file Makefile should at least contain the targets all, install, clean, and distclean. The all target is responsible for compiling all of the code necessary for the project. The install target is responsible for moving the compiled application to its final destination directory and changing permissions to make it executable if necessary. The clean target is responsible for removing any intermediate files left over from compiling. These tend to include source, object, and readme files generated by the compilation process. The distclean target is responsible for cleaning up (removing) any files that were not originally part of the distribution. Generally distributions only provide source code and a few instructions on how to compile. Everything else, including compiled and executable code, should be removed by the distclean target.

A closer look at the Makefile

The Makefile for our example application, named fermi, starts off with defining a number of macros. A macro is a name that represents a variable within the Makefile.

APP = main_fermi
MFILES = fermi.m
matlabversion = 7.10
DELETE_FILES =
BIN = ../bin
RP_DIR = $(shell . /etc/environ.sh; use -e -r rappture; echo $${RAPPTURE_PATH})

The APP, MFILES, matlabversion, DELETE_FILES, and BIN macros are defined at the top of our Makefile. Macros are assigned values by using the equal sign =, and their value is accessed using the dollar sign $. To assign a value to the APP macro, use APP = value. To get the value of the APP macro, use $(APP).

  • The APP macro holds the name of the application executable. It is also the name of the first function called when the executable is launched. This function is sets up use of rappture and calls the first function in the science code. In this example, the main_fermi function calls the fermi function.
  • The MFILES macro holds the names of all m-files that are a part of the source code and need to be included in the executable. The file names included in the MFILES macro include all of the m-files for the science code. Multiple file names should be separated by spaces.
  • The matlabversion macro holds the version of Matlab that should be used to compile the code.
  • The DELETE_FILES macro holds the list of files that should be removed when the "make clean" command is given. These are files that are created by the compilation process and are not part of the source code. If you use the "make clean" command and find there are files left in the directory that you did not create, you can add them to the DELETE_FILES macro.
  • The BIN macro holds the location of the bin directory where the executable files will be installed.

Generally, after filling in the above macros, the developer does not need to adjust the rest of the Makefile. Below provides further explanation about the other sections of the Makefile.


The next part of the Makefile describes how to compile the application target.

$(APP): $(APP).m $(MFILES)
    mcc -v -C -m -R -nojvm -I $(RP_DIR)/lib/matlab $(APP).m $(MFILES)
    patchrunscript.sh run_$(APP).sh run_$(APP).sh 1

In the target shown above, the values of the APP macro, $(APP), and the MFILES macro, $(MFILES), are substituted into the target when the target is run. To the make program, our target ends up looking like this:

main_fermi: main_fermi.m fermi.m
    mcc -v -C -m -R -nojvm -I $(RP_DIR)/lib/matlab main_fermi.m fermi.m
    patchrunscript.sh run_main_fermi.sh run_main_fermi.sh 1

Targets have the following form:

target: dependencies ...
    command(s)

In our example the target name is $(APP) (or main_fermi). This target depends on the files main_fermi.m and fermi.m. We list the dependencies on the same line as the target, after the colon (:). On the next line, indented by one tab, we list the command(s) to execute. For our example we want to invoke mcc, the MATLAB compiler. When we invoke the MATLAB compiler, we need to pass it some compiler options. The -v flag, enables verbosity and tells the compiler to show the compilation steps. The -m flag tells the compiler to generate a standalone application. The -R -nojvm flags tell the application not to start the java virtual machine at run-time. Normally when we start MATLAB with our uncompiled m-file, we would just use the -nojvm flag to tell MATLAB not to start the java virtual machine. The -R -nojvm flag does this for us at compile time. The -I flag adds the directory $(RP_DIR)/lib/matlab to the compiler's Include Path. Rappture's MATLAB Interface files are located in $(RP_DIR)/lib/matlab. After the compiler options, we list the m-files we want to compile. The mcc compiler uses the name of the first m-file as the name of the executable. To ensure uniformity, we place $(APP).m as the first m-file. Consequentially, the executable that mcc creates will be named $(APP).

As a part of the compilation process, mcc creates a script named run_$(APP).sh, where $(APP) is substituted as described above. We'll refer to this script as the run script. The run script prepares the shell environment for running the executable, and runs the executable. There are a few adjustments that need to be made in the file. The patchrunscript.sh script makes these adjustments for us automatically.

    patchrunscript.sh run_$(APP).sh run_$(APP).sh 1

patchsrunscript.sh takes at most three arguments. The first argument is the name of the input run script that needs to be adjusted. The second argument is the name of the output file of the adjusted run script. The third argument, which is optional, describes whether this tool uses Rappture's Matlab libraries. If the tool uses Rappture's Matlab libraries, the third argument should be 1, otherwise, it should be not provided at all.

The MATLAB compilation process creates a run script that can be used for launching the executable. To work with Rappture, the run script needs to be modified. The main purpose of patchrunscript.sh is to remove echo commands and properly setup the LD_LIBRARY_PATH to find the Rappture libraries the executable was compiled against.

There are more options for the mcc compiler. See the MathWorks MATLAB Compiler page for details. There is also additional information on make, Makefiles, and Makefile macros and targets in tutorials on the internet.

all:
    . /etc/environ.sh; use -e -r matlab-$(matlabversion); make $(APP)

all is next target in our Makefile. The all target is used to setup the compiling environment to include the version of the MATLAB compiler and associated libraries. With the compiling environment setup, it issues the "make $(APP)" command which compiles the MATLAB code.

The compilation process creates a few files in the directory. The most important of which include the executable, the run script, and the ctf file. In our fermi example, these are named main_fermi, run_fermi.sh, and main_fermi.ctf respectively. During the installation step, we will move the executable and run script into the final installation directory, $(BIN). If a ctf file was created, it will also be moved to the installation directory and extracted using the MATLAB program extractCTF.

install: all
    install --mode 0755 -D run_$(APP).sh $(BIN)
    install --mode 0755 -D $(APP) $(BIN)
    if [ -e $(APP).ctf ] ; \
        then mv $(APP).ctf $(BIN); \
    fi
    cd $(BIN); \
    if [ -e $(APP).ctf ] ; then \
        . /etc/environ.sh; \
        use -e -r matlab-$(matlabversion); \
        extractCTF $(APP).ctf; \
    fi
    install --mode 0644 -D readme.txt $(BIN)/$(APP).readme

The install target depends on the application having been compiled, which was accomplished by the all target. This is why all is on the dependency line for the install target.

clean:
    rm -f $(APP) $(APP).ctf \
              $(APP).prj run_$(APP).sh \
              $(APP)_main.c $(APP)_mcc_component_data.c \
              mccExcludedFiles.log readme.txt $(DELETE_FILES)

distclean: clean
    rm -rf  $(BIN)/$(APP) \
                $(BIN)/$(APP).ctf \
                $(BIN)/$(APP).readme \
                $(BIN)/$(APP)_mcr \
                $(BIN)/run_$(APP).sh

The last two sections of the Makefile are the clean and distclean targets. The clean target is responsible for removing any files that the compilation processes may have left behind. These are usually object files or intermediate source code files. They are not files we originally started with from our source code repository, nor are they files used during the execution of the program. Any files that are needed for running the program should have been installed in their appropriate installation directory. The distclean target calls the clean target and also removes binaries from the installation directory, $(BIN). After the distclean target runs, the only files that should be left in the directory structure are the original source files. There should be no binary executables, object files, or intermediate files left.

A closer look at the main function

For compiled MATLAB, we use a main function that opens the Rappture library file and calls the science code's first function. In the example below, the main function is main_fermi(), located in the main_fermi.m. The main_fermi() function looks like this:

function main_fermi(infile)

try
    lib = rpLib(infile);
    fermi(lib);
catch
    lastmsg = lasterr
    fid = fopen('errorDetected.txt','wt');
    fprintf(fid,'1\n');
    fprintf(fid,'%s\n',lastmsg);
    fclose(fid);
    append = 1;
    rpLibPutString(lib,'output.log',lastmsg,append);
    rpLibResult(lib,1);
end

The main_fermi() function accepts one argument, infile, which holds the directory path of the driver.xml file. infile is passed to the rpLib() function and the resulting Rappture library object is stored in the variable named lib. Next, the fermi() function is called with lib as an argument. The fermi() function is the first science code function. It accesses the variable lib to retrieve inputs from the graphical user interface and store rappture outputs.

If there is an error while opening the driver.xml file in the rpLib() function or in calling the science code, the catch statement logs the errors and returns them to the Rappture graphical user interface.

Installing

The make command takes targets as arguments. These targets can be chained together and make will run the targets in the order given on the command line.

To install, use:

make clean all install

or as separate commands:

make clean
make all
make install

Testing

The last part of the process is to test out the application to make sure the compilation and installation worked properly. Testing the application requires one modification to the tool.xml file and the creation of an invoke script. Inside of the tool.xml file, the command to execute should point to the run script located in the installation directory, $(BIN). For the fermi example, the beginning of our tool.xml file would look like this:

<?xml version="1.0"?>
<run>
    <tool>
        <about>Press Simulate to view results.</about>
        <command>@tool/../bin/run_main_fermi.sh @driver</command>
    </tool>
...

In the tool.xml file, the <command></command> tag now calls @tool/../bin/run_main_fermi.sh @driver. The location of the run script is specified relative to the location of the tool.xml file using @tool. The run_main_fermi.sh run script is used to start the compiled matlab code. It takes one argument. The argument is the name of the driver xml file. Since the name of this file is generated on the fly with a unique name, the @driver stands as a placeholder and before the MATLAB code is executed, the correct name is substituted in.

To launch the graphical user interface, an invoke script is needed. The invoke script lives inside of the middleware directory. The purpose of the invoke script is to properly setup the environment for running the simulation tool and to launch the simulation tool. Most invoke scripts simply call /apps/rappture/invoke_app and pass all of the command line arguments on to invoke_app. Here is a general purpose invoke script for starters.

#!/bin/sh

/usr/bin/invoke_app "$@" -t fermi

If your project does not already have an invoke script in the middleware directory, you can create copy the one above. Change the fermi value for the -t flag to the name of your own project. Place it in the middleware directory and make it executable using the chmod command:

chmod 755 middleware/invoke

Next launch the simulation tool by running the invoke script from the top of the project directory tree. Be sure to include the -T option and $PWD value to tell invoke where to look for the tool.xml file.

middleware/invoke -T $PWD

A graphical user interface should appear on the screen. Pressing the Simulate button should run the MATLAB code.

One common error is Unable to find tool.xml file for application. If invoke is unable to find the tool.xml file, check the rappture directory to make sure the tool.xml file exists there. If it does not exist there, put it there. If it does exist there, try giving the full path of the tool.xml file to the -T option of the middleware/invoke command.

A second common error is that the graphical user interface appears, but when the user presses the Simulate button, a message up saying there was a problem launching job because it can't execute .... This generally means there was a problem with the compilation and installation process. First check to make sure the run script it is trying to execute actually exists at the location it is looking for it, generally in the bin directory. If they do not exist, go back to the source directory and try compiling again. look for errors that come out after running:

make clean
make all
make install

Example

At the end of this wiki page, there is a gzipped tar file named matlab_compile_7.10.tgz that includes an example fermi calculating program written in MATLAB. From your workspace, you can download, uncompress, and try compiling the code.

Before starting, you should make sure you have the Rappture and Matlab environments properly setup. In the workspace, you can run the use command to do this:

use rappture
use matlab-7.10

When it asks if you want to make use rappture and use matlab-7.10 persistent, answer yes.

Here are the commands to run in your workspace xterm:

wget http://rappture.org/raw-attachment/wiki/FAQ_CompiledMatlab/matlab_compile_7.10.tgz
tar xvzf matlab_compile_7.10.tgz
cd matlab_compile_7.10/src

Open the Makefile and adjust the APP, MFILES, matlabversion, DELETE_FILES, and BIN macros as needed:

APP = main_fermi
MFILES = fermi.m
matlabversion = 7.10
DELETE_FILES =
BIN = ../bin

Next, compile the code:

make clean all install

The last step is to try and run the application using the invoke script located in the middleware directory. Use the -T option to specify the project directory which holds the tool.xml file. In this case we use $PWD to specify that the present working directory is where invoke can start searching for the tool.xml. By default, if invoke does not find the tool.xml in the directory specified as the argument to the -T option, it will look for the tool.xml in a directory named rappture under the provided directory.

cd ../
./middleware/invoke -T $PWD

Press the Simulate button in the Rappture graphical user interface, cross your fingers, and see if it worked.

Attachments

  • matlab_compile_7.7_x86.tgz (3.0 KB) - added by dkearney 4 years ago. gzip tar file containing proper project directory structure and example Makefile , matlab source, patch file for MATLAB 7.7 on 32Bit x86, tool.xml and invoke script.
  • matlab_compile_7.7_x86_64.tgz (3.0 KB) - added by dkearney 4 years ago. gzip tar file containing proper project directory structure and example Makefile , matlab source, patch file for MATLAB 7.7 on 64Bit x86_64, tool.xml and invoke script.
  • patchrunscript.sh (1.6 KB) - added by dkearney 2 years ago. This script modifies run_*.sh to include Rappture in the PATH and adjusts the MCRROOT environment variable. The script was written for MATLAB 7.12 and should work for 7.10 as well.
  • matlab_compile_7.10.tgz (2.2 KB) - added by clarksm 3 months ago. gzip tar file containing proper project directory structure, Makefile, MATLAB source program for the fermi application, tool.xml, and invoke script for MATLAB 7.10
  • matlab_compile_7.12.tgz (2.2 KB) - added by clarksm 3 months ago. gzip tar file containing proper project directory structure, Makefile, MATLAB source program for the fermi application, tool.xml, and invoke script for MATLAB 7.12