View Issue Details Jump to Notes ] Print ]
IDProjectCategoryView StatusDate SubmittedLast Update
0007832CMakeCMakepublic2008-10-20 11:332016-06-10 14:30
ReporterGeorge Neill 
Assigned ToKitware Robot 
PrioritynormalSeveritymajorReproducibilityhave not tried
StatusclosedResolutionmoved 
PlatformOSOS Version
Product VersionCMake-2-6 
Target VersionFixed in Version 
Summary0007832: add_subdirectory and a java project
Descriptiona 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.
Additional InformationI 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.
TagsNo tags attached.
Attached Filesgz file icon luajava.tar.gz [^] (305,295 bytes) 2008-10-20 11:33

 Relationships

  Notes
(0013908)
George Neill (reporter)
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 (reporter)
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 (manager)
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 (reporter)
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 (reporter)
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 (reporter)
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 (manager)
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 (reporter)
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 (reporter)
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 (reporter)
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 (reporter)
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 (reporter)
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 (reporter)
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 (administrator)
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.

 Issue History
Date Modified Username Field Change
2008-10-20 11:33 George Neill New Issue
2008-10-20 11:33 George Neill File Added: luajava.tar.gz
2008-10-20 13:08 George Neill Note Added: 0013908
2008-10-29 07:46 Robert Haines Note Added: 0013965
2008-10-29 13:41 David Cole Status new => assigned
2008-10-29 13:41 David Cole Assigned To => David Cole
2008-10-29 13:46 David Cole Note Added: 0013968
2008-10-29 13:47 David Cole Severity minor => major
2008-10-29 13:56 Robert Haines Note Added: 0013969
2008-10-29 14:01 Robert Haines Note Added: 0013970
2008-10-29 14:15 George Neill Note Added: 0013971
2008-10-29 14:33 David Cole Note Added: 0013972
2008-10-29 15:34 George Neill Note Added: 0013975
2008-10-30 06:43 Robert Haines Note Added: 0013984
2009-04-29 02:29 Tim Patterson Note Added: 0016236
2010-11-21 22:10 Mohit Aron Note Added: 0023472
2010-11-21 22:11 Mohit Aron Note Deleted: 0023472
2010-11-21 22:17 Mohit Aron Note Added: 0023473
2010-11-21 22:35 George Neill Note Added: 0023474
2010-11-21 22:51 Mohit Aron Note Added: 0023475
2011-02-09 14:26 David Cole Status assigned => backlog
2011-02-09 15:13 David Cole Assigned To David Cole =>
2016-06-10 14:27 Kitware Robot Note Added: 0041455
2016-06-10 14:27 Kitware Robot Status backlog => resolved
2016-06-10 14:27 Kitware Robot Resolution open => moved
2016-06-10 14:27 Kitware Robot Assigned To => Kitware Robot
2016-06-10 14:30 Kitware Robot Status resolved => closed


Copyright © 2000 - 2018 MantisBT Team