how to help? help me!

Dhek Bhun Kho bhun@chello.nl
Sat Dec 21 04:20:00 GMT 2002


Hi Rivas,

On Fri, 2002-12-20 at 21:48, Tom Tromey wrote:
> Rivas> - I have enought experience in java to colavorate with the development
> Rivas> of libjava or other related project
Me2, but posix/unix/linux is like a totally different piece of cake
sometimes. Things just work different.
> Rivas> - I WANT TO HELP!
Had the same idea. This is in the info I gathered so far from my first
journey into programming a non-virtual machine:

Java has a system layout like this:

JAVA_HOME/JDK_HOME
|		
|__
  |
  JRE
  |
  ___
     |
    LIB
     |__
        |
        EXT

Things usually get clearer when you compare it to your current system:
You're pc (like the hardware)
|		
|__
  |
  / (root filesystem) 
  |
  ___
     |
    /lib (called winnt/system on win32)
     |__
        |
        /usr/lib (can't find real analog for this, as win32 uses PATH to
resolve libraries)

...This will come in handy later. Back to compiling:

----------------------Simple ---------------

Let's take two classes to demonstrate the big difference between
compiling on gcc/gcj and javac (in sun style's guide):

class A { 
	A() {
		new B().hello(); 
	}

	public static void main (String[] args) {
		new A();
	}
}

class B {
	void hello() {
		System.out.println("hello");
	}
}

---> Java(Tm)->Native compilation(Tm)
Normally you specify compilation with 'your regular compiler'(Tm) like
this, assuming we are not using jars:

$$$ javac A.java

And everthing get's compiled, and you run it with (if you use the
commandline at all):

> $$$ java -classpath . A
> hello

This doesn't work for gcj, because it doesn't resolve the dependencies
for you. You have to specify (this is also in the manual):

$$$ gcj -o A A.java B.java --main=A

-o: give the executable binary a name, you are not restricted to "A"
--main: you have to define the main method that is going to launch your
program.

And run it with:

> $$$ ./A
> hello

So this is one case where gcj differs, another thing is that gcj doesn't
really care about locations in connection with package names. Normally
you are require by gcj to locate all subpackages in a directory that's
called the same as you're package

----------------------Simple with packages ---------------

Assuming the following classes as an example (in prehistoric optimized
style):

class A { A() { (new b.B()).hello();} public static void main(String[]
s){ new A()} }
package b; class B { B() { System.out.println("hello");}}

you would have to have a subdirectory called B to get this compiled with
javac (unless you do some twiddling with the jar packager), when the
source tree is  located in  src/:

javac --sourcepath src/ src/A.java

With gcj it doesn't matter:

gcj -o A A.java B.java --main=A

You just have to the package declartion. It's worth mentioning that you
cannot name the classes the same as the package they are in. 

----------------------JAR vs. SO (library death match) -

In the disgaram on to I typed in a little layout (it's better than
seeing all binary code going over google). The point is that when you
launch a program using a JVM it usually searches the following paths
first:

CLASSPATH: jre/lib, jre/lib/ext

this equals to:

LD_PRELOAD: /lib, /usr/lib

Anything you specify yourself on the classpath using the CLASSPATH
variable get's appended to the first two directories, you achieve the
same effect by:

CLASSPATH=/libdir/A.jar:/libdir/B.jar
LD_LIBRARY_PATH=/libdir

You don't need to specify the library names.
We of planet Java (a very beautiful planet indeed) usually just create
libraries:

$$$ javac -sourcepath srcdir/ srcdir/A.java srcdir/b/B.java
$$$ jar cvf myJar.jar -C srcdir/ .

Somewhere on the black planet called legacy :D, they do this: 

$$$ gcj --shared -o libmyjar.so A.java B.java
(--shared: create a library, comparable to a jar)

Well taking the last examples of A and B, in Java we could have included
a manifest with Main-Class: A as an attribute (you can also specify jars
as a classpath variable->Classpath:):

$$$ jar cvfm manifest myJar.jar -C srcdir/ .

To do this with gcj, you can package everything as in the last gcj
compilation, and then create the main entry point just like with the
manifests using:

$$$ gcj -o executable --main=A s.so

--- Very worth mentioning

1. The compilation of java->native and java->source->native produces
different results. Sometimes errors are specific to the compilation of
java->native, some to java->class and some to class->native.

2. Deeply nested try..try..try...try.. try.. blocks will kill gcj
(3.2.1)

3. Calling methods in a abstract superclass that are inherited by
another abstract superclass will have bad results (gcj 3.2.1),
workaround is to add explicit abstract method declarations.

4. There is an issue with some case statements up till gcj 3.2.1

5. StringTokenizer and SerializationFields will not work correctly.

6. Gcj does not seem to share the same concept as the regular jvm of
separated namespace for classloaders for each different codebase and
refuses to initialize if you load a class that is already present in
libgcj.so (much like the systemclasses in the jvm, they are always
loaded locally, I wonder what happened to jaxp in jdk1.4.1, if that
implies the same change, would be pretty irritating). This is the
org.w3c.dom.Attr duplicate class key registration phenomenon :P.
Somebody else already posted it, but the context wasn't clear. I had the
same problem when compiling Ant. I wanted xslt support so I compiled all
the classes form xerces and xalan. To get around this, don't include
classes already present in libgcj into your own shared libraries.

7. Sometimes you need to explicitly (as has been noted earlier on the
list and in gnats) qualify a classname e.g.:
instead of extends A--> extends package.package.package.A
This is quite easy to do in something like netbeans (haven't tried
eclipse yet).

8. Static compilation doesn't seem possible. 

9. The problem with having to specify every source file can be solved
ysing makefiles. You can emit dependecy lists by incrementally compiling
your source tree and specify -MM to gcj (there are some other options
too). Another way to solve it is to go shell scripting. It can be quite
bothersome if you aren't used it. Ant isn't fully functional when
compiled with gcj 3.2.1 or less because of the buggy StringTokenizer,
this can be fixed by compiling you're gcj and replacing the sourcefiles
in the cvs source. 

Greets Bhun.

PS> for some reason I haven't been able to get sonames working, with
-Wl,-soname. please correct me if I got anything wrong.



More information about the Java mailing list