3

How can I get mcc to recognize imports from user-provided Java libraries, or to simply ignore unresolvable imports?

I have a Matlab codebase that I'm building with the Matlab Compiler, but the build is breaking because mcc is erroring out when it encounters import statements for Java classes that were in JARs on Matlab's dynamic classpath. I am including all the JAR files on the classpath with the mcc -a option. The code works in the IDE, and I think it will work in the deployed app, if it will only allow me to build. (Works under R2009b, which ignores these imports in non-MCOS classes.)

Here's a simple repro. This file is in the same dir as guava-11.0.1.jar from Google Guava.

%file hello_world_with_import.m
function hello_world_with_import
import com.google.common.base.Stopwatch;
disp('Hello, world!');
end

Running it in Matlab works fine. But building it fails. (The javaaddpath here is not strictly necessary in the example, because bad imports by themselves are not an error in plain Matlab. Just showing how it works in practice, and how I wish mcc picked up on it.)

>> javaaddpath('guava-11.0.1.jar');
>> hello_world_with_import()
Hello, world!
>> mcc -m -a guava-11.0.1.jar hello_world_with_import
Error: File: C:\Temp\import_test\hello_world_with_import.m Line: 3 Column: 8 
Arguments to IMPORT must either end with ".*" 
 or else specify a fully qualified class name: "com.google.common.base.Stopwatch" fails this test. 
Unable to determine function name or input/output argument count for function  
in MATLAB file "hello_world_with_import".  
Please use MLINT to determine if this file contains errors. 
Error using mcc
Error executing mcc, return status = 1 (0x1).

This is in Matlab R2011b on Windows.


Some background on my environment. My app has about 40 JARs on the dynamic classpath which are a mix of third party libraries and our own Java code. It's deployed to 50+ users on a mix of single-user and multi-user Windows machines. And there are other groups that may be deploying other MCR apps to the same users and machines. On any machine, different MCR apps may be run concurrently by the same or different users. We do weekly releases, and (mostly due to changes in our own Java code) at least one JAR file changes about every other release. I need a mechanism that will work in this environment.


Any suggestions? Anybody know a good way to get mcc to add stuff to its java classpath in the compilation step, or just ignore bogus imports? My fallback plan is to go through the codebase and remove all the imports for Java classes, which is kind of a pain.


UPDATE 12/2/2012: I heard from MathWorks that this is fixed in Matlab R2012b. (But I'm no longer using Matlab so can't personally verify it.)

UPDATE 12/09/2014: I'm using Matlab again (R2014b), and the Matlab Compiler now includes JARs that are on the dynamic classpath in the compiled program's dynamic classpath. It doesn't seem to automatically include the JAR files in the archive, though; you must manually include them using an mcc command line switch, or adding them as "additional included files" in the Matlab Compiler app.

Andrew Janke
  • 23,508
  • 5
  • 56
  • 85

3 Answers3

4

The code executing in the MATLAB IDE works because the guava jar file has been added to the "dynamic" classpath via the javaaddpath method. However, when you use MCC to invoke the MATLAB Compiler, it does not rely on the dynamic java classpath, but the "static" java classpath which is defined in:

$MATLABROOT/toolbox/local/classpath.txt

If you add an entry for your JAR file here, then MCC will be able to resolve the IMPORT line in your M-File.

So to test this, I downloaded the guava jar file and tried the steps above. Works like a charm.

Also, If you read the "Troubleshooting" section for the MATLAB Compiler, this exact situation is documented:

http://www.mathworks.com/help/toolbox/compiler/brtm1xm-8.html

Quoting from the link: "The import statement is referencing a Java class () that MATLAB Compiler (if the error occurs at compile time) or the MCR (if the error occurs at run time) cannot find.

To work around this, ensure that the JAR file that contains the Java class is stored in a folder that is on the Java class path. (See matlabroot/toolbox/local/classpath.txt for the class path.) If the error occurs at run time, the classpath is stored in matlabroot/toolbox/local/classpath.txt when running on the development machine."

eternaln00b
  • 1,043
  • 9
  • 18
  • +1 Having end users modify the system libraries isn't an acceptable solution for wide deployment. They won't necessarily have the technical know-how or file permissions to do so, different programs may use different sets and versions of libraries, and it adds variance to the environment. But it looks like you're on to something here. I'm going to try modifying the static classpath file just in the build environment long enough to get it to compile with the imports, and see if it'll then pull the JARs on the the dynamic classpath at run time of the deployed MCR app. – Andrew Janke Feb 10 '12 at 07:52
  • @AndrewJanke, well, if you're giving them the compiled application, they must install the MCR anyway, correct? So I don't think permissions is such a big deal. As for technical know-how of the end user, you could write a small utility to perform some post-setup, like say, a shell script that does the required things, including modifying the classpath.txt that is in the MCR based on knowledge of where the MCR was installed. – eternaln00b Feb 10 '12 at 13:09
  • Also, please consider accepting my answer since I believe it is technically the correct answer to your question. :) – eternaln00b Feb 10 '12 at 13:10
  • The users don't actually install the MCR or the deployed app themselves. The MCR is installed once by our sysadmins when a new machine is set up. The themselves don't have file permissions to modify that or other applications. The compiled app is installed to network drives as part of the build process. – Andrew Janke Feb 10 '12 at 14:12
  • But putting aside the issues of user maintenance and file permissions, modifying the JARs in the static classpath of the MCR installation itself is not suitable for large deployments of non-toy applications. At any one point in time, a user (or concurrent users on Windows Server) might be running different MCR apps against a given MCR which require different, incompatible sets of JARs. Could be my app and an app from Bob in Accounting down the hall, two different releases of my app (for side-by-side comparison), or even my app and a third party MCR app over which we have no control. – Andrew Janke Feb 10 '12 at 14:23
  • @AndrewJanke, you make very valid points. Let me chew on this a little and i'll get back to you if I can think of a nice solution. – eternaln00b Feb 10 '12 at 14:32
  • @AndrewJanke, I have another suggestion. Take it with a pinch of salt because I haven't actually tried this, but it SHOULD work. When building the app, place the location of the JAR on your static class path as usual. Also, package the JAR file with the app. Now, when deploying the application, just ensure that the end users have the MCR_CACHE_ROOT env variable setup. If they do, then the JAR file can be dynamically added to the class path on end user machines because you will also say "javaaddpath([MCR_CACHE_ROOT, '...']" in your M-File. – eternaln00b Feb 10 '12 at 14:45
  • 2
    I think we've got a solution here. I set the static classpath for mcc by having the build script generate a `classpath.txt` file in the build dir (instead of modifying the Matlab install) that included all JARs from my dynamic classpath, and mcc was happy with the imports. The MCR_CACHE_ROOT wasn't even necessary; the build script pre-extracts the CTF archive so the javaaddpath() calls could pull in the JARs from the detected install location. Edit the answer to remove the stuff about modifying the user's MCR install (which I can't endorse) and I'll give you the accept. – Andrew Janke Feb 11 '12 at 00:05
  • :) Impressive. Edited the answer to remove references to forcing the end-user to edit the classpath.txt. I only talk about your specific issue now, which is the issue with the import statements on the dev machine. – eternaln00b Feb 11 '12 at 02:03
  • The link in the answer is broken now. Here's a similar one to a bug report on their site which is still live: http://www.mathworks.com/support/solutions/en/data/1-GX7QR1/index.html?ref=ts_rR2012b – Andrew Janke Dec 02 '12 at 22:20
2

You just have to put import statements in a separate .m file.

so from:

javaaddpath 'c:\some.jar';
import com.something.Element;
...interesting stuff...

There will be a do_imports.m:

import com.something.Element;

And in original .m:

javaaddpath 'c:\some.jar';
do_imports
...interesting stuff...

And then it will compile and work. No need to mess around with system-wide classpaths.

Zsolt
  • 29
  • 1
  • This would not have worked, because `mcc` in that version does not execute the javaaddpath statement, so the import statement will still not resolve and the build will break. – Andrew Janke Mar 20 '14 at 06:23
-1

Here is an extract from the link http://blogs.mathworks.com/desktop/2009/07/06/calling-java-from-matlab/

MATLAB maintains a path for Java classes separate from the search path. That means even if you have a .class or .jar file on the MATLAB path, unless you use javaaddpath you will not be able to use it. To see what is currently on the path use javaclasspath. Running this command you will show you a long list of files that ship with matlab called the Static Class Path and then you'll see the Dynamic Class Path. The dynamic class path is where classes added to the path with javaaddpath will be placed. They can be removed with javarmpath and have to actively reloaded each session of matlab.

Sajan Chandran
  • 11,287
  • 3
  • 29
  • 38
  • This doco is only referring to Matlab itself, not `mcc`. This is how I get my JARs on the classpath in Matlab. But I need `mcc`, as called from Matlab, to respect it too. – Andrew Janke Feb 09 '12 at 23:54
  • @AndrewJanke mcc only respects the static class path, and not the dynamic class path (modified using javaaddpath), see my answer above. – eternaln00b Feb 10 '12 at 03:00