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.