Re: simple compilaton question

From: Chris Marshall (christopherlmarshall_at_yahoo.com)
Date: 05/10/04


Date: 10 May 2004 08:16:40 -0700

cppaddict <hello@hello.com> wrote in message news:<fefl9092i65f3vuu85ftmngp37hj5a75t6@4ax.com>...
> 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
christopherlmarshall@yahoo.com
http://www.hopelesscase.com:81/gcj
2004-04

>>>1. INTRODUCTION

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 christopherlmarshall@yahoo.com. 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.

>>>2. SUN'S BUILD SYSTEM

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.

>>>3. BUILDING PROGRAMS MANUALLY WITH GCJ

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.

>>>4. THIS FILES IN THIS PACKAGE AND HOW THEY WORK

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).

>>>5. THEORY OF OPERATION

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
christopherlmarshall@yahoo.com



Relevant Pages

  • Re: I need advice about GUI platform for new project
    ... Java Beans, or Swing, or whatever with an interface to C++. ... If he's planning to compile with only gcc, he could also use the "CNI" ... for use with GCJ are still buggier than the Sun implementation (not ...
    (comp.lang.java.gui)
  • Re: Java server wont run after reboot
    ... Working with classpaths in Java isn't too hard, ... package definitions in your source code. ... Execute from that directory, no classpath definition needed. ... Assuming that you compile your code in the same directory and deposit the ...
    (RedHat)
  • Re: Java programmer lured back by .Net (Questions)
    ... I have finally decided to put all my business logic in C++ and compile that to native code. ... Security is the only reason why I could see that being a problem but I'm sure there is a way around it. ... Of course it learned a lot from Java, and improved upon many things which Java is slowly catching up on. ... I would like to be able to place my executable on my remote server and then "load" the executables on demand from accross the internet - so that there are no executables on the local machine for prying eyes to reverse engineer. ...
    (microsoft.public.dotnet.distributed_apps)
  • Re: Standard Database Interface?
    ... Windows binary and examples athttp://aspn.activestate.com/ASPN/Cookbook/Tcl?kwd=TclBlend. ... use native tcl and binary packages. ... and Java i.e Tcl/TK 8.4.13 and Java 1.4.2.12. ... patient enough to show me a step by step method on how to compile ...
    (comp.lang.tcl)
  • Re: How to avoid the message "Unknown source"?
    ... You haven't provided any source code and you haven't even identified the ... If you are getting a compiler error, javac, the java compiler, should be ... If you are geting a compile ... exception message that will give you a clue about your problem. ...
    (comp.lang.java.help)