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/depend2For 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.