MantisBT - CMake
View Issue Details
0007832CMakeCMakepublic2008-10-20 11:332016-06-10 14:30
George Neill 
Kitware Robot 
normalmajorhave not tried
closedmoved 
CMake-2-6 
 
0007832: add_subdirectory and a java project
a java project does not build .class/.jar files correctly when listed in conjuntion with an ADD_SUBDIRECTORY() command.

The javac/jar command line paths appear to be wrong.
I have attached three projects, (NOTE, these examples are from the luajava opensource project)

1) luajava-1.1-compiles, A working example of a java project (no subdirectories), but .jar file is not generated correctly (notable only when I try to generate a javah'd header from it).

2) luajava-1.1-broken, A broken example (with subdirectories) Won't compile at all.

3) luajava-1.1-proposed, A proposed fix (with subdirectories). To me this is just a temporary fix (*hack*) to get it working. There's probably some C++ work that needs to be done here.
No tags attached.
gz luajava.tar.gz (305,295) 2008-10-20 11:33
https://public.kitware.com/Bug/file/1787/luajava.tar.gz
Issue History
2008-10-20 11:33George NeillNew Issue
2008-10-20 11:33George NeillFile Added: luajava.tar.gz
2008-10-20 13:08George NeillNote Added: 0013908
2008-10-29 07:46Robert HainesNote Added: 0013965
2008-10-29 13:41David ColeStatusnew => assigned
2008-10-29 13:41David ColeAssigned To => David Cole
2008-10-29 13:46David ColeNote Added: 0013968
2008-10-29 13:47David ColeSeverityminor => major
2008-10-29 13:56Robert HainesNote Added: 0013969
2008-10-29 14:01Robert HainesNote Added: 0013970
2008-10-29 14:15George NeillNote Added: 0013971
2008-10-29 14:33David ColeNote Added: 0013972
2008-10-29 15:34George NeillNote Added: 0013975
2008-10-30 06:43Robert HainesNote Added: 0013984
2009-04-29 02:29Tim PattersonNote Added: 0016236
2010-11-21 22:10Mohit AronNote Added: 0023472
2010-11-21 22:11Mohit AronNote Deleted: 0023472
2010-11-21 22:17Mohit AronNote Added: 0023473
2010-11-21 22:35George NeillNote Added: 0023474
2010-11-21 22:51Mohit AronNote Added: 0023475
2011-02-09 14:26David ColeStatusassigned => backlog
2011-02-09 15:13David ColeAssigned ToDavid Cole =>
2016-06-10 14:27Kitware RobotNote Added: 0041455
2016-06-10 14:27Kitware RobotStatusbacklog => resolved
2016-06-10 14:27Kitware RobotResolutionopen => moved
2016-06-10 14:27Kitware RobotAssigned To => Kitware Robot
2016-06-10 14:30Kitware RobotStatusresolved => closed

Notes
(0013908)
George Neill   
2008-10-20 13:08   
I didn't mention specifically ... but when the project does -not- have the add_subdirectory() command, the .jar file doesn't seem to be packed properly. (extra paths added).
(0013965)
Robert Haines   
2008-10-29 07:46   
I can confirm this bug and that the workaround above works - although only tested with the Makefile generator on Linux.

I can also confirm that jar files are built with some of the CMake build machinery in them when the above workaround is not used.

I'd be tempted to mark this bug as "major" rather than "minor" as, if you're building jar files, it is a complete showstopper!
(0013968)
David Cole   
2008-10-29 13:46   
I agree... this should be categorized as "major"...

To fix this without the "*hack*" connotation, would it suffice to make the "override.cmake" rules the default rules that CMake uses in CMakeJavaInformation.cmake or is there something else required?

Perhaps it is the MakeDirectory call in the override.cmake file that you think should go into the C++ source of CMake...?

It's certainly not a hack to make sure all the *.class files end up in their own directory without any other baggage, is it?

I guess I would just like you to clarify what it is about this solution that you think is a *hack*...

Thanks,
David Cole
(0013969)
Robert Haines   
2008-10-29 13:56   
It's not my "hack" but I think it solves the problem rather neatly. I guess that so long as what it is doing is consistent with how CMake does things in the general case then it's all good and could go in CMakeJavaInformation.cmake.

Building the class files into their own directory is certainly the way to go as you point out. javac handles the problem of putting the built .class files into the right structure below that so CMake doesn't need to do anything on top of that (which is I think where the whole process was falling down in the first place)
(0013970)
Robert Haines   
2008-10-29 14:01   
Sorry for the noise, but

One thing I would change is that I wouldn't have the classes built into a hidden directory as it is in George's original (.class). I'd have it in full view like you would any other built object ("classes" maybe). After all, the class files might be what you want rather than a jar and hiding them would be odd in that case.
(0013971)
George Neill   
2008-10-29 14:15   
Hi David,

I gave it the *hack* connotation. Without knowing much of the cmake c++ internals, I assumed there should be some sort of <OBJECTS> (<CLASSES> maybe?) tag instead of wildcarding some directory (as done in the CMAKE_Java_CREATE_STATIC_LIBRARY rule). If something like <OBJECTS> correctly exists, the manufactured directory I created would not be relavant.

What I have provided is just an alternative way to get it working in what I thought was correct, it may or may not be the ultimate solution.

HTH,
George.
(0013972)
David Cole   
2008-10-29 14:33   
Well.... then we make a good team, because I am not that much of a Java guy. From what I *do* understand about *.java / *.class files, though, I was under the impression that there could be multiple *.class files that come out of a single *.java file compile. Is that correct?

If so, then we have to do it "the directory way"... If not, we could use <OBJECTS> and make sure that it expands correctly for the java jar archiver.

Is there a guaranteed one-to-one correspondence between .java and .class files?

Or, is there at least a 99%+ common usage pattern that says there should be a one-to-one correspondence? (Just because it's a good design principle...)

If we do use <OBJECTS>, what is the correct syntax for passing multiple *.class files to the jar archiver? (The -d arg would not be used in that case, correct?)
(0013975)
George Neill   
2008-10-29 15:34   
Unfortunately, I am not a java expert either ... however I am willing to help out as much as I can, even if I need to dive in to the cmake core.

Of course the one *important* fact I forgot about, you are correct about multiple .class files can be generated from one source file. javac will generate one .class file for each class defined in the .java source. I believe the standard java-way is to have one class per .java file.

Off the top of my head, I am not sure how to detect/tackle the multiple .class file issue. Quite possibly, just wildcarding the directory is the appropriate way.

I will do some research.

On the jar archiver, here's a couple of examples from the javac help.

Example 1: to archive two class files into an archive called classes.jar:
       jar cvf classes.jar Foo.class Bar.class
Example 2: use an existing manifest file 'mymanifest' and archive all the
           files in the foo/ directory into 'classes.jar':
       jar cvfm classes.jar mymanifest -C foo/ .
(0013984)
Robert Haines   
2008-10-30 06:43   
Again, not a Java expert, but I've written quite a bit of it...

The standard way is to have one *public* class per .java file. You can have as many other classes (and inner classes) in the .java file as you like and they all get put in their own .class file when built. Inner classes expand to something like MainClass$InnerClass.class when built and when you get to anonymous inner classes you can have tens of them generated from a single class.

It might also be nice to be able to specify a manifest file to be added to the jar as it's created although I'm not sure how easy it would be to do this without making changes to the add_library command.
(0016236)
Tim Patterson   
2009-04-29 02:29   
I've just attempted to use the fix you propose and it builds well, however the dependencies don't reflect change and still point to the expected output directories.

eg. C:\HelloWorld.java is expected to produce C:\HelloWorld.class, but the output file now goes into C:\.class\HelloWorld.class

I'm not sure where the output of the compile step is calculated; I assume it is in the generator somewhere?
(0023473)
Mohit Aron   
2010-11-21 22:17   
The original fix proposed in this bug report has several issues:
(1) The dependences don't reflect the change. Thus, repeated 'make' invocations would compile the .java files unnecessarily each time.
(2) If a given CMakeLists.txt has multiple ADD_LIBRARY() calls, then the jar file produced as a result of the 2nd ADD_LIBRARY() call would erroneously also contain the .class files from the first one.

Given below is a fix that we use at my company, and that addresses the above issues. The only assumption we make is that all Java files must belong in a package that starts with 'com.'. (You can suitably tune this solution in case your packages start with say 'org.' or if you have multiple kinds of packages - some that start with 'com.' and some that start with 'org.').

First, we have a custom shell script that compiles Java files rather than javac. This shell script actually invokes javac, but it fixes the arguments that cmake provides to javac. Let's call this script javac_wrapper.sh. Here are its contents:


#!/bin/bash

args=""
modify_next=0

for i in "$@" ;do
  if [ x"$i" = x"-d" ] ;then
    modify_next=1
    args="$args $i"
  elif [ $modify_next -eq 1 ] ;then
    modify_next=0
    # Remove everything after and including the last '/com/'.
    args="$args ${i%/com/*}"
  else
    args="$args $i"
  fi
done

exec $JDK_HOME/bin/javac $args


One can make cmake use the above shell script as the Java compiler by putting the following in the top level CMakeLists.txt:

SET(CMAKE_Java_COMPILER "/path/to/javac_wrapper.sh")


The above fix alone is sufficient in correcting the jar files that cmake produces. The only remaining issue is that these jar files also contain extraneous cmake files. To fix this, we'll override the jar call that cmake uses . Thus, we prepare a Override.cmake file that has the following contents:

set(CMAKE_Java_CREATE_STATIC_LIBRARY
  "<CMAKE_Java_ARCHIVE> -cf <TARGET> -C <OBJECT_DIR> com")

Additionally, we add the following to the top level CMakeLists.txt to make it load the Override.cmake file:

set(CMAKE_USER_MAKE_RULES_OVERRIDE "Override")

That's it. This produces nice .jar files for us. All the cmake dependencies are also preserved.
(0023474)
George Neill   
2010-11-21 22:35   
I really think someone needs to look at the CMake internals to make this work better. For instance, the patch Mohit has provided will not work on Windows.
(0023475)
Mohit Aron   
2010-11-21 22:51   
Under windows, the patch I provided would work under cygwin. But it is true that cmake really ought to fix this by now without requiring such hacks on top. This bug was opened in 2008 and despite Java's popularity, cmake does not support it well even today.
(0041455)
Kitware Robot   
2016-06-10 14:27   
Resolving issue as `moved`.

This issue tracker is no longer used. Further discussion of this issue may take place in the current CMake Issues page linked in the banner at the top of this page.