.class file source dependency

P

Paul Geisler

Hello,


i just wonder about what a .class file depends on. For example, if in a
classpath of many files, I change one .java source file. Is it enough to
compile only this source file? Or will there be changes in other
dependent .class files as well?


thanks very much

Paul
 
M

Martin Gregorie

i just wonder about what a .class file depends on. For example, if in a
classpath of many files, I change one .java source file. Is it enough to
compile only this source file? Or will there be changes in other
dependent .class files as well?
Surely that depends on what changes you made.

If its a purely internal bug fix that doesn't affect the API or
alter any returned values, then nothing else should need to be changed
or recompiled.

However, if the change altered the API and/or the value returned by at
least one method, then all dependencies need to be examined, changed as
needed, and all affected code should be recompiled.

You should be careful if you use ant. It only recompiles classes whose
source is more recent than the class file, i.e. it has no make-style
dependency tree, so it will do the right thing in the first case, but may
not in the second unless you force a complete recompile.
 
T

Tom Anderson

i just wonder about what a .class file depends on. For example, if in a
classpath of many files, I change one .java source file. Is it enough to
compile only this source file? Or will there be changes in other
dependent .class files as well?

There might be. It depends if the change that was made affects the
signature of the class it was in, or only the implementation. By
'signature', i mean:

- Whether the type is a class or an interface

- Whether the class is final or not

- Whether the class is abstract or not

- The superclass of the class

- The set of interfaces implemented by the class

- The names of any methods defined by the class

- The return types of any methods defined by the class

- The parameter types of any methods defined by the class

- The values of any public compile-time constant expressions (see JLS
section 15.28, but basically, public constants of primitive or string type
with definitions that can be resolved by the compiler)

And probably a few other things that don't spring to mind right now.

In some cases, changes to these will break other classes - obviously,
renaming a method called from another class will cause that class to
break. In others, the other class is still okay, but the bytecode
generated for it will change - the old bytecode won't work, the source
code will compile, and the new source code will work. For instance:

public class A {
public static void print(int x) {
System.out.println(x);
}
}

public class B {
public static void main(String... args) {
A.print(23);
}
}

If you change 'int' to 'long' in A, an attempt to run B will bomb out with
a NoSuchMethodError. Recompiling B will fix this.

When constants are involved, there are even cases where the old code will
run but do the wrong thing:

public class A {
public static final int X = 23;
}

public class B {
public static void main(String... args) {
System.out.println(A.X);
}
}

If you change A.X to 17, recompile A, and run B, it will still print 23.

tom
 
A

Arne Vajhøj

Paul said:
i just wonder about what a .class file depends on. For example, if in a
classpath of many files, I change one .java source file. Is it enough to
compile only this source file? Or will there be changes in other
dependent .class files as well?

It can be very difficult to analyze that.

The simplest and in my opinion best solution is to:
- delete all class files
- recompile everything

That will save you from some unpleasant troubleshooting sessions.

Arne
 
M

markspace

Tom said:
- The values of any public compile-time constant expressions (see JLS
section 15.28, but basically, public constants of primitive or string
type with definitions that can be resolved by the compiler)


Tom's list was pretty good. The bit about constant expressions I quoted
just above is the usual big "gotcha" that most folks will encounter.
Everything else will throw a runtime exception. Constants just get "out
of sync" and cause weird, hard-to-track-down behavior.

Most folks don't seem to know this: Ant also has a depend task that will
find these sorts of interdependencies and recompile when they are
encountered, rather than just relying on file time stamps.

For example:

<target name="compile">
<depend srcdir="${src.dir}">
destdir="${build.dir}">
closure="yes" />
<javac srcdir="${src.dir}" destdir="${build.dir}" />
</target>

Will preform a deeper analysis and catch those sorts of issues. This
takes extra time, of course, but can be a life saver if you are having
problems keeping dependencies straight during the compile phase. This
particular task, with closure="yes" just deletes any .class files which
need recompiling, so as to force the javac task to compile them.

Besides "closure", there's also attributes which let you specify a cache
directory ("cache") and one to print the results out ("dump", which
takes a yes/no parameter) and you can give the depend task a classpath
too ("classpath").
 
J

John B. Matthews

markspace said:
Tom's list was pretty good. The bit about constant expressions I quoted
just above is the usual big "gotcha" that most folks will encounter.
Everything else will throw a runtime exception. Constants just get "out
of sync" and cause weird, hard-to-track-down behavior.

LOL. When you're re-factoring your own code, chances are you modify both
affected classes at the same time. When you're new, chances are that
you're told to use some public constant. More than once this has
translated into, "nothing worked; so I just deleted it all and checked
in out again; then it worked."
Most folks don't seem to know this: Ant also has a depend task that will
find these sorts of interdependencies and recompile when they are
encountered, rather than just relying on file time stamps.

For example:

<target name="compile">
<depend srcdir="${src.dir}">
destdir="${build.dir}">
closure="yes" />
<javac srcdir="${src.dir}" destdir="${build.dir}" />
</target>

Will preform a deeper analysis and catch those sorts of issues. This
takes extra time, of course, but can be a life saver if you are having
problems keeping dependencies straight during the compile phase. This
particular task, with closure="yes" just deletes any .class files which
need recompiling, so as to force the javac task to compile them.

Besides "closure", there's also attributes which let you specify a cache
directory ("cache") and one to print the results out ("dump", which
takes a yes/no parameter) and you can give the depend task a classpath
too ("classpath").

Excellent! Not quite a clean build; more of a sponge bath:)

<http://ant.apache.org/manual/OptionalTasks/depend.html>
 
P

Paul Geisler

Hi!

Martin said:
However, if the change altered the API and/or the value returned by at
least one method, then all dependencies need to be examined, changed as
needed,
that is obvious.
and all affected code should be recompiled.
ok, but a code could be affected even if not changed, as i now see.

Tom Anderson (see later replys) gives some examples. for example, if the
type of an argument changes to a compatible one (eg. int to long), the
calling source files would not change, but the method's signature does,
so the callers need to be recompiled if i understand that right..

so its not really obvious, when a file has to be recompiled or not i think?


many thanks

Paul
 
P

Paul Geisler

that strongly approve my thought that first of all software should be
reduced in complexity...

if everything compiles in half a minute, no one need to care for
dependency aware build mechanisms :)


thanks,

paul
 
M

Martin Gregorie

ok, but a code could be affected even if not changed, as i now see.
As others have pointed out, if a competent single programmer is making
the change the problem doesn't arise because, if the change affects the
class signature, all classes that depend on this class will normally be
examined and changed to match.

This leaves problems that result from alterations that change returned
values or (particularly insidious) changing the value of a public final
attribute.
so its not really obvious, when a file has to be recompiled or not i
think?
Not really, hence the common use of the command "ant clean; ant" in those
circumstances (assuming build.xml contains a 'clean' target that deletes
all .class files.

Of course, now we've been made aware of the depend task, this should be a
bit easier.
 
A

Arne Vajhøj

Paul said:
that is obvious.

ok, but a code could be affected even if not changed, as i now see.

Tom Anderson (see later replys) gives some examples. for example, if the
type of an argument changes to a compatible one (eg. int to long), the
calling source files would not change, but the method's signature does,
so the callers need to be recompiled if i understand that right..

so its not really obvious, when a file has to be recompiled or not i think?

I still believe that delete all class files and compile everything
will save you time in the long run.

Arne
 
L

Lew

Arne said:
I still believe that delete all class files and compile everything
will save you time in the long run.

Or a hybrid approach - frequent recompilation of one or a few classes during
active development with periodic clean builds as you reach programming plateaus.

What you'd do in IDE mode, i.e., while developing stuff, is one pattern; what
you do for nightly or continuous tests is another; other scenarios include
testing and production deployment.

Knowing the tradeoffs and limitations in the build process, one can properly
balance expediency with thoroughness.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,756
Messages
2,569,533
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top