simple compilaton question

Discussion in 'Java' started by cppaddict, May 6, 2004.

  1. cppaddict

    cppaddict Guest

    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
     
    cppaddict, May 6, 2004
    #1
    1. Advertising

  2. cppaddict wrote:

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

    --
    Kind regards,
    Christophe Vanfleteren
     
    Christophe Vanfleteren, May 7, 2004
    #2
    1. Advertising

  3. cppaddict <> wrote in message news:<>...
    > 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

    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 . 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
     
    Chris Marshall, May 10, 2004
    #3
  4. cppaddict

    Dale King Guest

    Hello, Christophe Vanfleteren !
    You wrote:

    > cppaddict wrote:
    >
    > > 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.

    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.

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

    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.
    --
    Dale King
    My Blog: http://daleking.homedns.org/Blog
     
    Dale King, Apr 15, 2006
    #4
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    0
    Views:
    585
  2. Kevin Spencer

    Re: Simple Simple question!!!

    Kevin Spencer, Jun 25, 2004, in forum: ASP .Net
    Replies:
    0
    Views:
    694
    Kevin Spencer
    Jun 25, 2004
  3. Daniel Frey

    Simple Question - Simple Answer?

    Daniel Frey, Dec 28, 2004, in forum: XML
    Replies:
    4
    Views:
    871
    Daniel Frey
    Jan 12, 2005
  4. Oli

    simple simple question

    Oli, Jan 26, 2004, in forum: ASP General
    Replies:
    10
    Views:
    390
    Roland Hall
    Jan 26, 2004
  5. Peter Bailey

    simple, simple array question

    Peter Bailey, Apr 8, 2008, in forum: Ruby
    Replies:
    7
    Views:
    236
    Peter Bailey
    Apr 8, 2008
Loading...

Share This Page