Previous Section up Next Section

Chapter 9. Java

Many Java developers like Integrated Development Environments (IDEs) such as Eclipse. Given such well-known alternatives as Java IDEs and Ant, readers could well ask why they should even think of using make on Java projects. This chapter explores the value of make in these situations; in particular, it presents a generalized makefile that can be dropped into just about any Java project with minimal modification and carry out all the standard rebuilding tasks.

Using make with Java raises several issues and introduces some opportunities. This is primarily due to three factors: the Java compiler, javac, is extremely fast; the standard Java compiler supports the @filename syntax for reading "command-line parameters" from a file; and if a Java package is specified, the Java language specifies a path to the .class file.

Standard Java compilers are very fast. This is primarily due to the way the import directive works. Similar to a #include in C, this directive is used to allow access to externally defined symbols. However, rather than rereading source code, which then needs to be reparsed and analyzed, Java reads the class files directly. Because the symbols in a class file cannot change during the compilation process, the class files are cached by the compiler. In even medium-sized projects, this means the Java compiler can avoid rereading, parsing, and analyzing literally millions of lines of code compared with C. A more modest performance improvement is due to the bare minimum of optimization performed by most Java compilers. Instead, Java relies on sophisticated just-in-time (JIT) optimizations performed by the Java virtual machine (JVM) itself.

Most large Java projects make extensive use of Java's package feature. A class is declared to be encapsulated in a package that forms a scope around the symbols defined by the file. Package names are hierarchical and implicitly define a file structure. For instance, the package a.b.c would implicitly define a directory structure a/b/c. Code declared to be within the a.b.c package would be compiled to class files in the a/b/c directory. This means that make's normal algorithm for associating a binary file with its source fails. But it also means that there is no need to specify a -o option to indicate where output files should be placed. Indicating the root of the output tree, which is the same for all files, is sufficient. This, in turn, means that source files from different directories can be compiled with the same command-line invocation.

The standard Java compilers all support the @filename syntax that allows command-line parameters to be read from a file. This is significant in conjunction with the package feature because it means that the entire Java source for a project can be compiled with a single execution of the Java compiler. This is a major performance improvement because the time it takes to load and execute the compiler is a major contributor to build times.

In summary, by composing the proper command line, compiling 400,000 lines of Java takes about three minutes on a 2.5-GHz Pentium 4 processor. Compiling an equivalent C++ application would require hours.

    Previous Section up Next Section