simple compilaton question

C

cppaddict

Hi,

Say I'm compiling a class Main.java, which begins with some import
statements:

import package1.ClassA;
import package2.ClassB;

and say that I have the files ClassA.java and ClassB.java, but not
their associated .class files. Will the command:

javac Main.java

automatically compile ClassA and ClassB first, or do I have to
manually do:

javac ClassA.java
javac ClassB.java

before I try to compile Main?

Is there a way to have the imported files automatically recompiled?
Should I use a Makefile? What are my other options?

Thanks in advance,
cpp
 
C

Christophe Vanfleteren

cppaddict said:
Hi,

Say I'm compiling a class Main.java, which begins with some import
statements:

import package1.ClassA;
import package2.ClassB;

and say that I have the files ClassA.java and ClassB.java, but not
their associated .class files. Will the command:

javac Main.java

automatically compile ClassA and ClassB first, or do I have to
manually do:

javac ClassA.java
javac ClassB.java

before I try to compile Main?

Ooh ooh, I know it. How about you actually try this? It can be done faster
than the time it took you to type your post.

Short answer, yes, javac is smart enough to compile referred classes.
Is there a way to have the imported files automatically recompiled?
Should I use a Makefile? What are my other options?

They don't need to be recompiled, if they are not changed in a way that
would be binary incompatible (there's a section in the JLS about what
constitutes a non binary compatible change).

If you want a build tool that has Makefile like properties, check out Ant
http://ant.apache.org/, it is the de facto standard java build tool.
 
C

Chris Marshall

cppaddict said:
Hi,

Say I'm compiling a class Main.java, which begins with some import
statements:

import package1.ClassA;
import package2.ClassB;

and say that I have the files ClassA.java and ClassB.java, but not
their associated .class files. Will the command:

javac Main.java

automatically compile ClassA and ClassB first, or do I have to
manually do:

javac ClassA.java
javac ClassB.java

before I try to compile Main?

Is there a way to have the imported files automatically recompiled?
Should I use a Makefile? What are my other options?

Thanks in advance,
cpp

I use Sun's JDK on linux and the way I have dealt with this is to run
"javac *.java" in each of my java source file directories the first
time and to run javac file.java after I make a change to a particular
java file.

Recently, I've tried to get smarter about doing this by using the make
tool.

I have written a Makefile and two supporting bash scripts (make_dep1
and make_dep2) to handle the generation of dependency information, and
then to use that information to only recompile the java files that
require it, given the specific source files that were changed and when
they were changed.

I have written the system up and made it available on my website at:

http://www.hopelesscase.com:81/gcj

I've attached the write-up below, in case you find it helpful.

Chris Marshall

A Simple Makefile and Build System for Java Projects Using the GNU
Java Compiler

by Chris Marshall
(e-mail address removed)
http://www.hopelesscase.com:81/gcj
2004-04

This set of files is a template I use to compile my java programs
with gcj. I offer it in the hopes that someone else will find
it useful in making the switch from Sun's JDK to gcj, in the cases
where that makes sense for them. While Sun's JDK is ahead of gcj in
some areas, gcj is ahead of Sun's JDK in other areas. Two of those
areas are that gcj doesn't limit your redistribution rights, and
it allows you to compile your programs to native executable binaries,
which can be very useful.

It took me several attempts (lasting over a year from first to last)
to figure out how to use gcj in an equivalent fashion to how I had
been using Sun's JDK. Part of the reason it took so long is I was
slow to ask for help on mailing lists. The gcj mailing list has to
be one of the most helpful out there. Don't be afraid to subscribe
and ask the simplest of getting-started questions.

In addition to handling java source files, the build system handles
C source files in a similar way. C++ will be a straight forward
extension when I get around to it. I have placed some hooks for this
already.

If you have any questions about how to get this working, please email
me
at (e-mail address removed). I need your help in working out
the
bugs I know are there but haven't discovered yet, both in the build
scripts and in my attempt to write it up so people unfamiliar with
GCC and related tools (like make) get start using it with a minimum of
fuss.

Let's start by reviewing how you use the JDK's javac and java commands
to
compile and load java programs.

Let's say we have our java code in our home directory, under a
directory
called "project." We have two packages, A, and B, with one source
file each
(X.java, and Y.java). Let's also say X refers to methods in Y and Y
refers to
methods in X. Here are the files:

~/project/A/X.java
~/project/B/Y.java

You could A/X.java everything like this:
cd ~/project
export CLASSPATH=~/project
cd A
javac X.java

As long as your CLASSPATH was set to ~/project, you could run A/X from
anywhere like this
java A.X

For a larger project (with, say, a dozen package directories and
hundreds of java files),
the compiling procedure is to cd to each package directory and run the
command:
javac *.java

Running any program is as simple as
java <package>.<mainclass>

Note how well the procedure scales. Writing a bash script to do this
straight forward.

Here is what the above example looks like when using gcj (the GCC Java
compiler/linker front-end).

cd ~/project
gcj --classpath=. -c A/X.java -o A/X.o
gcj --classpath=. -c B/Y.java -o B/Y.o
gcj --main=A.X -o A/X A/X.o B/Y.o

That would take us through linking the A/X program. You could then
run the program
like you could any native binary:

~/project/A/X

Linking and running B/Y would be:

gcj --main=B.Y -o B/Y B/Y.o A/X.o
~/project/B/Y

Since X has been compiled and linked into a native executable, you can
also
copy ~/project/A/X to any directory you want to and run it from there.
If
you copy it to a place on your PATH, like /usr/loca/bin/X, then you
can run
it from anywhere by just typing X.

Unlike the above method for Sun's JDK, this gcj manual procedure
doesn't scale.
The tough part is the link step:

gcj --main=A.X -o A/X A/X.o B/Y.o

We need some way of figuring out which java files A/X.java depends on,
then we need
to convert those filenames to .o files, and list them after the "-o
A/X" part of the
link command. By the time you get to 12 packages and 150 java files,
those lists
can get quite large. Maintaining them by hand is out of the question.

Fortunately, gcj can make all of this information available to you
with the -MD
(make dependency) flag. If you went through the compile step with the
-MD flag
like this:

cd ~/project
gcj -MD --classpath=. -c A/X.java -o A/X.o

then, in addition to creating the object file A/X.o, gcj would also
create a
make-style dependency file A/X.d that looked like this

A/X.o: A/X.java /usr/share/java/libgcj-3.2.3.jar ./B/Y.java

Which is saying that the object file A/X.o depends on A/X.java, the
libgcj
standard library jar file, and the additional java file B/Y.java.

By processing A/X.d and A/Y.d, we could generate the list of object
files we
needed in the link step.

That's the bulk of what I've done with my build system. Written two
bash scripts and a Makefile (two, actually. One that lives in
~/project and one that
lives in each package directory) to process the .d files that gcj is
so kind as to
generate for us.

When it is in place, you build your entire project like this:

cd ~/project
make depend
make all

I've also written the standard clean and install targets.


Here are the files in this package:

../Makefile
../make_dep1
../make_dep2
../deps
../A/Z1.c
../A/Z1.h
../A/W1.c
../A/W1.h
../A/X1.c
../A/X1.h
../A/Makefile
../A/Z.java
../A/W.java
../A/X.java
../B/Makefile
../B/Y1.c
../B/Y1.h
../B/Y.java

Notice the mixture of Java and C files.
Java and C files are not mixed in the sense that they link against
each other, but that C and Java files may exist in the same
directories
and the build scripts I have written will handle them in the same way.
The heart of the system is in these files:

../Makefile
../make_dep1
../make_dep2
../deps
../A/Makefile
../B/Makefile

To apply these scripts to another java/c project, you would copy the
first three
files to your top level project directory, and you would copy
A/Makefile to
each of your package directories and edit it (mostly filling in the
cfiles and jfiles
variables to list the c and java files that you want linked into
separate programs;
more on that shortly). Before you try to do that, however, for
heaven's sake, read
my warning about the role of the "deps" file. You will be glad you
did.

The .c, .h, and .java files are the simplest set of source files that
illustrate the problems
this build system solves (mostly generating the list of object files
you need to link into your
executable).

The files ./A/X1.c and ./A/X.java both contain main functions and we
want the build process to
figure out all of the dependencies required to build those two
programs. X.java
calls a static method in W.java which in turn calls a static method in
Z.java.
However, X.java doesn't refer to any function in Z.java directly,
which makes the
automatic generation of dependencies tricky. The C files are
similarly constructed,
but with 1's attached to their filenames. You can't have C and Java
files with the same
names in the same package directory with this build process.

Without further explanation, here is how you invoke make to build
those two
programs:

make depend
make all

***IMPORTANT!*** The "deps" file, written to by make in the "make
depend" step,
has to exist or make won't let you do anything and you won't be able
to figure
out the error message. In the event that nothing seems to be working
and you are
presented with bizarre error messages, try to remember this warning
and check
that "deps" exists. It is perfectly fine if it is empty as long as it
exists
before you try to do a "make depend." In fact, if you look at the
"clean" target
in ./Makefile, you will notice that the last thing it does it delete
deps, then
create it as an empty file.

You can run A/X and A/X1 from to verify they work. Both just print a
simple message
(the number 2, followed by the number 1, to be exact).

My strategy is to use empty files with special names to pass
information between successive
make invocations and the bash scripts. Writing scripts in this style
can be a very powerful
way to get work done. When not documented, however, it can be almost
impossible to figure out
how it works in a complex case.

I use empty files named *.e to mark which main source files should be
built into
stand-alone programs. If you run

make proglist

../Makefile goes to each subdirectory that has a Makefile in it and
runs "make proglist"
in it.

Let's examine ./A/Makefile

# we list the main source files for each program here
cfiles=X1.c
jfiles=X.java

efiles=${cfiles:=.e} ${jfiles:=.e}
prgs=${cfiles:.c=} ${jfiles:.java=}

proglist :
touch ${efiles}

clean :
rm -f ${prgs} ${efiles}

When you invoke this Makefile with "make proglist", it takes the list
of c files
in the variable cfiles, and the list of java files in jfiles, adds the
..e suffix
to each one, then creates empty files with those names. In this case,
it will create
X1.c.e and X.java.e. ./make_deps2 will look for files that end in .e
and strip the .e
to arrive at the name of the source file we want to be linked into a
stand alone program.
The program will be name will be determined by stripping the .java or
..c suffix.

To edit A/Makefile for your use, you only have to change the
definition of cfiles and jfiles.
For example:

cfiles=W.c X.c Y.c
jfiles=A.java B.java C.java

This is as far as I have gotten in writing this up. I'll be back in a
few days with more.

Chris Marshall
(e-mail address removed)
 
D

Dale King

Hello, Christophe Vanfleteren !
You said:
Ooh ooh, I know it. How about you actually try this? It can be done faster
than the time it took you to type your post.

Short answer, yes, javac is smart enough to compile referred
classes.

In some cases it can, such as this one, assuming you have your
source organized correctly. But in larger cases it won't
automatically compile everything that is out of date. Consider if
ClassA referenced ClassC and ClassC was out of date but ClassA
was not, then ClassC would not be recompiled. So while javac's
ability to do some limited recompilation is helpful it is no
substitute for a build system that analyzes dependencies to make
sure that everything that needs to be built is built.

And note if the OP had simply tried it as you suggested he would
not have known about these limitations.
They don't need to be recompiled, if they are not changed in a way that
would be binary incompatible (there's a section in the JLS about what
constitutes a non binary compatible change).

If you want a build tool that has Makefile like properties, check out Ant
http://ant.apache.org/, it is the de facto standard java build
tool.

NO! Ant most definitely does not have Makefile like properties!
Make is designed to analyze dependencies between files to rebuild
everything that needs to be rebuilt. Ant definitely does not do
that. It is trivial to make changes to source file and have Ant
not rebuild everything that needs to be rebuilt.

Unfortunately, make does not work well with Java. It is possible
to have circular dependencies in Java, which might even require
compiling more than one file at the same time. Make will not work
with circular dependencies.

Your best bet is a decent IDE like Eclipse that handles all the
dependencies for you. For command line builds check out Javamake
(which can be invoked as an ant task) which is the only command
line tool i know of to guarantee correct Java compilation.
 

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,535
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top