This page is very out of date
Below are some notes about makefile dependencies for Java files.
.java file is likely
to have dependencies on other .java files in different
packages. If the .java file in the other package changes
we would like the local file to be recompiled automatically.
Bidirectional dependencies should be avoided. If two packages are dependent upon each other, it makes it hard to determine which package to compile first. Below are the possibilities
javac at the same time.
A subpackage is a package that is beneath a parent package in the
directory hierarchy. For example ptolemy.kernel.util is a
subpackage of ptolemy.kernel. The makefiles are structured so
that we run make in the subdirectories first so in general, it is best
if parent packages depend on their subpackages.
Below are several possible solutions
javac Foo.java
will compile
files that are directly depended on and missing or out-of-date (see
the javac manpage).
Another alternative is to run javac *.java in each
directory. The problem is that
javac *.java takes a few minutes to complete, which
is painful if we are trying to compile just one file.
javac man page says
-depend
Causes recompilation of class files on which the source
files given as command line arguments
recursively depend. Without this option, only files that are
directly depended on and missing or
out-of-date will be recompiled. Recompilation does not
extend to missing or out-of-date files
only depended on by already up-to-date class files.
The Java Programmer's Faq has a question about dependencies, but it is not much help:
7. (Sect. 3) I'm working on a project with lots of classes and I use the
JDK. A recompile from scratch takes forever when I do it a class at a
time. How do I recompile everything?
[*] The first way is
javac *.java
Another way is
javac -depend tip.java
where "tip.java" is a class "at the tip of the iceberg", i.e. that
depends on (uses) all the other classes. Typically, this may be your
main class. However, "-depend" is known to be buggy and cannot be
relied upon. It also doesn't issue compile commands in parallel to make
use of multi-processor systems.
Without the "-depend" option, the standard "javac files" doesn't look
beyond the immediately adjacent dependencies to find classes lower down
the hierarchy where the source has changed.
The -depend options searches recursively for depending classes and
recompiles it. This option doesn't help when you have dynamically
loaded classes whose names cannot be determined by the compiler from
the dependency graph. E.g. you use something like
Class.forName(argv[0]);
The author of the code using those classes should make sure that those
classes are mentioned in a Makefile.
As a test, I compiled
Director.java
(a class from the
Ptolemy II system)
3 times without
-depend, and got
times of 3.31, 3.29 and 3.20 seconds.
When I compiled the same file with -depend, I got times of 17.30, 16.46 and 16.40 seconds
Obviously, this is not very scientific, but it is also obvious that
-depend slows things down by 4 or 5 times.
As a test, I rebuilt my Ptolemy II tree without -depend
and it took 11
minutes. This number is not exact, but it is a damn sight faster than
the usual 45 minutes :-)
-xdepend option will print out
dependencies. However, this option is gone in JDK 1.2.
-depend is now -Xdepend
cxh@carson 35% /opt/jdk1.2fcs/bin/javac -X -Xdepend Recursively search for more recent source files to recompile -Xstdout Send messages to System.out -Xverbosepath Describe how paths and standard extensions were searched -JPass argument to the java interpreter The -X and -J options are non-standard and subject to change without notice.
http://developer.javasoft.com/developer/techDocs/SearchData/dd/jdk1.2/477.html says:
I wonder why javac from jdk1.2-beta3 does't have the -depend option anymore, it was renamed to -Xdepend. Is this because it is beta, or is sun working on a new make system? Thanks Martin Answers ======= ** From: kriff: Replied: Fri Dec 04 11:16:22 PST 1998 The change is meant to reflect the fact that not all java compilers accept the -depend option. Sun is trying to standardize the command line options used by Java compilers so that Java tools can easily invoke them no matter who created them. Some switches (like -classpath) must be supported by any compiler, however other non-standard options may be available. Non-standard options will now always start with an 'X' to indicate that they are not available everywhere. The behavior of -depend is unchanged.
To illustrate the difference between javac -depend
and regular javac, we have three classes
A.java,
B.java and
C.java
The constructor of A calls the constructor of B, which in turn calls
the constructor of C
cxh@carson 194% ls
A.java B.java C.java
cxh@carson 195% more *.java |cat
::::::::::::::
A.java
::::::::::::::
public class A {
public A() {
B b;
System.out.println("A()");
b = new B();
}
public static void main(String args[]) {
A a = new A();
}
}
::::::::::::::
B.java
::::::::::::::
public class B {
public B() {
C c;
System.out.println("B()");
c = new C();
}
}
::::::::::::::
C.java
::::::::::::::
public class C {
public C() {
System.out.println("C() cough");
}
}
If we touch
the .java files and run
javac -verbose A.java, then we can see that all three
files get compiled:
cxh@carson 196% setenv CLASSPATH . cxh@carson 197% touch A.java B.java C.java cxh@carson 198% javac -verbose A.java [parsed A.java in 319 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/Object.class) in 62 ms] [checking class A] [parsed ./B.java in 2 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/System.class) in 21 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/PrintStream.class) in 15 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/String.class) in 29 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/Serializable.class) in 2 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/FilterOutputStream.class) in 3 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/OutputStream.class) in 3 ms] [wrote A.class] [checking class B] [parsed ./C.java in 2 ms] [wrote ./B.class] [checking class C] [wrote ./C.class] [done in 1653 ms] cxh@carson 199%If we run
javac -verbose A.java again,
then only A.class gets generated. This is because
B.class exists and is up to date.
cxh@carson 199% javac -verbose A.java [parsed A.java in 335 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/Object.class) in 61 ms] [checking class A] [loaded ./B.class in 4 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/System.class) in 19 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/PrintStream.class) in 15 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/String.class) in 30 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/Serializable.class) in 2 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/FilterOutputStream.class) in 3 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/OutputStream.class) in 3 ms] [wrote A.class] [done in 1620 ms] cxh@carson 200%If we touch
C.java, or remove C.class
then java -verbose A.java will not recompile
C.java
cxh@carson 200% touch C.java cxh@carson 201% javac -verbose A.java [parsed A.java in 326 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/Object.class) in 64 ms] [checking class A] [loaded ./B.class in 4 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/System.class) in 20 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/PrintStream.class) in 16 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/String.class) in 30 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/Serializable.class) in 1 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/FilterOutputStream.class) in 4 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/OutputStream.class) in 3 ms] [wrote A.class] [done in 1597 ms] cxh@carson 202% rm C.class cxh@carson 203% javac -verbose A.java [parsed A.java in 318 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/Object.class) in 61 ms] [checking class A] [loaded ./B.class in 4 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/System.class) in 19 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/PrintStream.class) in 15 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/String.class) in 29 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/Serializable.class) in 2 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/FilterOutputStream.class) in 3 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/OutputStream.class) in 3 ms] [wrote A.class] [done in 1578 ms] cxh@carson 204%If we touch
B.java, then java -verbose A.java
will recompile B.java and then go on to recompile
C.java because B.java depends on it
cxh@carson 204% touch B.java cxh@carson 205% javac -verbose A.java [parsed A.java in 329 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/Object.class) in 62 ms] [checking class A] [parsed ./B.java in 3 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/System.class) in 21 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/PrintStream.class) in 16 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/String.class) in 30 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/Serializable.class) in 2 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/FilterOutputStream.class) in 3 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/OutputStream.class) in 3 ms] [wrote A.class] [checking class B] [parsed ./C.java in 3 ms] [wrote ./B.class] [checking class C] [wrote ./C.class] [done in 1732 ms] cxh@carson 206%Now, if we touch
C.java and use the -depend
option, (i.e. java -verbose -depend A.java),
then C.java gets recompiled.
Note that this is different than the behaviour we saw above
with java -verbose A.java.
cxh@carson 206% touch C.java cxh@carson 207% javac -verbose -depend A.java [parsed A.java in 317 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/Object.class) in 61 ms] [checking class A] [loaded ./B.class in 6 ms] [parsed ./B.java in 3 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/System.class) in 20 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/PrintStream.class) in 16 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/String.class) in 30 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/Serializable.class) in 2 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/FilterOutputStream.class) in 4 ms] [loaded /usr/java1.1/lib/classes.zip(java/io/OutputStream.class) in 3 ms] [wrote A.class] [loaded /usr/java1.1/lib/classes.zip(java/lang/Class.class) in 26 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/CloneNotSupportedException.class) in 2 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/IllegalArgumentException.class) in 2 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/Integer.class) in 14 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/InterruptedException.class) in 3 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/StringBuffer.class) in 17 ms] [loaded /usr/java1.1/lib/classes.zip(java/lang/Throwable.class) in 8 ms] [checking class B] [parsed ./C.java in 2 ms] [wrote ./B.class] [checking class C] [wrote ./C.class] Output truncated to save space -cxh
To see the difference between javac -depend
and regular javac in Ptolemy II,
run the C-shell commands below:
setenv CLASSPATH ../..
cd $PTII/ptolemy; make clean
cd $PTII/ptolemy/actor
javac -verbose TypedIORelation.java >& /tmp/nodepend1
# Process the output to get rid of the timing info
awk '{print $1, $2}' /tmp/nodepend1 | sort > /tmp/nodepend2
cd $PTII/ptolemy; make clean
cd $PTII/ptolemy/actor
javac -verbose -depend TypedIORelation.java >& /tmp/depend1
# Process the output to get rid of the timing info
awk '{print $1, $2}' /tmp/depend1 | sort > /tmp/depend2
# Do a diff
diff /tmp/nodepend2 /tmp/depend2
For example, javac -depend compiles
ptolemy/graph/DirectedAcyclicGraph but
javac without -depend does not.
The reason is that data/TypeLattice includes
a private method
private static DirectedAcyclicGraph _setup()and a static variable:
private static DirectedAcyclicGraph _typeLattice = _setup();TypeLattice is compiled by both compilation methods.
In this case, java -depend would protect us
against problems in DirectedAcyclicGraph, such as having missing methods
The JavaMemberDepend application, which is part of the Sun CDC,
see: http://java.sun.com/j2me/docs/pdf/CDCFoundation_Porting_Guide.pdf
http://java.sun.com/products/cdc/
cd cdcfoundation/src/share/javavm/jcc javac -classpath ".;../test" JavaMemberDepend.java
java -classpath ".;../test" JavaMemberDepend -classpath d:/tmp/ptII/ptolemy/copernicus/java/cg/OrthogonalCom/tst -loadClass ptolemy.copernicus.java.cg.OrthogonalCom.Main -showAll
http://www.experimentalstuff.com/Technologies/JavaMake/
is java dependency tool written in Java that uses javac to manage
a database of file dependencies
However, the JavaDepend home page is dead.
http://pluto.njcc.com/~slinky/land_of_science.html
Jamie Marconi <jmarconi@sea.hp.com> was kind enough to send
me a copy
(JavaDepend_v2.4.5.zip)
Jamie Marconi is thinking of setting up a JavaDepend web page.
(Local users can view the unzipped JavaDepend distribution at
~ptII/devel/JavaDepend).
JavaDepend uses Java Generic Library (JGL) version 3.1.0,
which is available from
http://www.objectspace.com
Local users can view the JGL installation at
~ptII/devel/jgl3.1.0.
The JGL license says
Licensee may not distribute in any form of electronic or printed
communication the materials within or otherwise related to JGL
that bear the ObjectSpace copyright, including but not limited
to the source code, documentation, help files, examples, and
benchmarks, without prior written consent from ObjectSpace,
Inc. Send any requests for limited distribution rights to
jgl@objectspace.com.
The Licensee may distribute binaries (.class files) derived from
or contained within JGL ("Binaries") provided that:
1) The Binaries are not integrated, bundled, combined, or
otherwise associated with a Java development environment or
Java development tool; and
2) The Binaries are not a documented part of any distribution
material.
Below are a few questions about JGL:
makedepend approach and create
a makefile that is based on a make.template
but has the dependency rules tacked on to the end of the file.
One interesting thing about JavaDepend is that .java files
are dependent on .class files instead of being dependent on .java files.
I'm not sure why this is, but javac apparently reads
.class files, and only compiles .java files if they are out of date
with regard to the .class files
~ptII/devel/JavaDepend/examples/1/Makefile
To use JavaDepend, I edited ptcommon.mk and added
############## # Rules for using JavaDepend # JGL_HOME = /users/ptII/devel/jgl3.1.0 JD_HOME = /users/ptII/devel/JavaDepend JGL_CLS = $(JGL_HOME)/lib/jgl3.1.0.jar JD_CLS = $(JD_HOME)/WARREN.zip JDCLASSPATH = $(JGL_CLS):$(JD_CLS) depend.mk : $(JSRCS) CLASSPATH=$(CLASSPATH):$(JDCLASSPATH) $(JAVA) -Drules=$(ROOT)/mk/jddefault.mk -Dmakestream=$@ -Dfiles=" $(JSRCS) " WARREN.tools.JavaDepend include depend.mkI also commented out the .SUFFIXES rule and set it to null
#.SUFFIXES: .class .java .SUFFIXES:
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
ptolemy/kernel directory, and the .class
files in ptolemy/kernel/util do not exist, then
JavaDepend creates a depend.mk file includes text like:
ComponentEntity.class : ComponentEntity.java \ Entity.class \ CompositeEntity.class \ /usr/java1.1/lib/classes.zip \ Port.class \ ComponentPort.class rm -f `basename $< .java`.class CLASSPATH="$(CLASSPATH)$(AUXCLASSPATH)" $(JAVAC) $(JFLAGS) $< # WARNING: [ComponentEntity.java, 75] Cannot resolve dependency for possible type Workspace.class, so none created # WARNING: [ComponentEntity.java, 94] Cannot resolve dependency for possible type IllegalActionException.class, so none created # WARNING: [ComponentEntity.java, 94] Cannot resolve dependency for possible type NameDuplicationException.class, so none created # WARNING: [ComponentEntity.java, 122] Cannot resolve dependency for possible type Nameable.class, so none createdI think this is a short coming in JavaDepend, since it looks like it is depending on the presence of the .class files in the subdirectory. It should be able to look for the .java file instead
The workaround here is to create the .class files in the subdirectory before creating the depend.mk file in the current directory. This is sort of lame.
One side effect of this workaround is that since GNU make automatically
builds depend.mk, if we run
make clean, then the .class files get built and
then we remove them.
This workaround fails for files like
ptolemy/kernel/util/test/PtestWorkspace, which depend
on a class in the parent directory. We get:
make[2]: *** No rule to make target `../../../../ptolemy/kernel/util/PtolemyThread.class', needed by `PtestWorkspace.class'. Stop. make[2]: Leaving directory `/export/carson/carson2/cxh/ptII/ptolemy/kernel/util/test'and
make[1]: *** No rule to make target `../../../ptolemy/kernel/CompositeEntity.class', needed by `TopologyEvent.class'. Stop. make[1]: Leaving directory `/export/carson/carson2/cxh/ptII/ptolemy/kernel/event'One solution would be to hack in rules that would cd to the appropriate directory and run make there. This could cause problems with two packages that are dependent on each other.
depend.mk file which depends
on the JDK zip file
depend.mk.
lib/tools.jar
instead of lib/classes.zip?
make to completely
build the system. If a .class file has other files that depend on it,
then each time the .class file is built, the other files will be rebuilt
as well.
depend.mk is created, we get warnings like:
../../../../mk/ptcommon.mk:347: depend.mk: No such file or directoryOne workaround is to add a '
-' before the
include depend.mk:
-include depend.mkHowever, I think the leading dash will only work with GNU make. An alternative is to use
sinclude depend.mkbut this fails with Solaris
/usr/ccs/bin/make
/usr/ccs/bin/make
cxh@carson 34% make NamedObj.class make: Warning: Infinite loop: Target `Nameable.class' depends on itself make: Warning: Infinite loop: Target `Nameable.class' depends on itself rm -f `basename .java`.class CLASSPATH="../../.." /usr/java1.1/bin/javac -g use: javac [-g][-O][-debug][-depend][-nowarn][-verbose][-classpath path][-nowrite][-deprecation][-d dir][-JThe problem is that the rule uses] file.java... *** Error code 1 make: Fatal error: Command failed for target `InvalidStateException.class'
$<, which Solaris
/usr/ccs/bin/make is failing to interpret correctly
http://developer.javasoft.com/developer/bugParade/bugs/4175374.html says:
Javac make(1) dependency generation
Category general:compiler
Reported Against: 1.2beta4
Release Fixed:
State: In progress, request for enhancement
Submit Date Sep 22, 1998
Description
Like all good C/C++ compilers, javac should offer
a command line option for generating makefile
dependencies. This should be very easy to do for
the compiler and would be a very cool feature for
all of us guys managing big projects with the help
of make(1).
Thanks, tom.
(Review ID: 37053)
Workaround
None.
Evaluation
Hi Tom,
It is not straightforward to write make rules for general java programs.
I wanted to add a feature like this but ran into troubles...
In C or C++, your interfaces are put into separate header files. In
make you add dependencies on the header files to represent a
dependency on an external api. In java, you have no separate header
files. If class Foo and Bar depend on each others external api's, then
you have two .java files which depend on each other. This is not
something that happens with .c files. Also, compile-time constants are
mandated by the JLS to be inlined. Class Foo may inline a compile
time constant from Bar and class Bar may also inline a compile time
constant from Foo. (In the C/C++ world, this is akin to having two
header files both depend on each other's #defines -- doesn't work.) If
both Foo.java and Bar.java have been modified, then you may need
to compile both simultaneously (same command line) in order to get
the correct results. I don't know of a straightforward rule system for
make which can build up these cliques of sources which must be
compiled simultaneously and issue them in the same command.
I realize this is a short explanation. I think that a make-like tool for
java would be very valuable for java programmers. I believe that it
will probably have to be integrated into the compiler to get it to be
robustly correct.
xxxxx@xxxxx 1998-09-28
.java files and generates
.class files. It is a replacement for javac,
and does not include the Java runtime (classes.zip).
Jikes has a +M option which will generate Java dependencies.
Local users can view information about the local Jikes installation
http://www.vet.com.au/java/javadeps/ active website
http://www.cs.mcgill.ca/~stever/software/JavaDeps original website
http://www.bmsi.com - Local Copy
To generate a list of classes:
java -classpath ~cxh/public_html/java/javadepend:$PTII ZipLock ptolemy.domains.sdf.demo.orthocom.OrthogonalComSystem
To create a jar file cd $PTII; jar -cf orthocom.jar `java -classpath ~cxh/public_html/java/javadepend:$PTII ZipLock ptolemy.domains.sdf.demo.orthocom.OrthogonalComSystem`
http://depfind.sourceforge.net/ says:
The goal of this project is to have a tool, or suite of tools, that can extract class dependencies from compiled Java code. The tools can get all the necessary information out of the .class files.
cxh at eecs
Copyright © 1998, The Regents of the University of California.
All rights reserved.