LinuxDevCenter.com

oreilly.comSafari Books Online.Conferences.

We've expanded our Linux news coverage and improved our search! Search for all things Linux across O'Reilly!

Search
Search Tips

advertisement

Listen Print Subscribe to Linux Subscribe to Newsletters

Advanced makefiles
Pages: 1, 2

Shell functions and more make functions

Make is not an ideal tool for Java compilation. The Java compiler likes to resolve dependencies itself, which can cause make some confusion. When resolving dependencies, Java works from the source files and make works from the target. Java's structure invites the casual creation of new source files, and make is designed for new source files to be added manually.



In an ideal world, each new file would be added to the make script as part of a variable definition. This project is done in the real world, so make searches the source directories and constructs the list of targets automatically. Because of Java's idiosyncrasies and some aspects of our project, this requires four variables and a mix of shell and make functions.

In the previous version of this makefile, I generated the targets with an external shell script, exporting the list to a file as make variables, then importing the make variables into the makefile. Mitch improved my makefile so targets are generated in a much neater way.


# Search the directory containing the sources, and 
# generate a corresponding name for the generated 
# .class file. Make sure the classes in example/util/* 
# are compiled first.
CLASSFILES_view:=
  $(patsubst %.java,$(class_d)/%.class,
  $(shell find example/util -name '*.java'))

# Find the non-util, non-views sources.  Uses 
# -path -prune instead of grep -v, from an example 
# on the find(1) man page.
CLASSFILES_nonview:=    
  $(patsubst %.java,$(class_d)/%.class,
  $(shell find example -path example/util -prune -o 
  -path example/views -prune -o -name '*.java' -a -print))

# Find the classes generated by xmlc from the 
# .html files, except the ones in the mockup 
# directory.
CLASSFILES:=    
  $(CLASSFILES_view) $(CLASSFILES_nonview)
VIEWFILES:=     
  $(patsubst %.html,$(class_d)/%.class,$(shell find 
  example -path example/views/mockups -prune -o -name 
  '*.html' -a -print))

This makefile includes shell functions as well as standard make functions. The syntax for a shell function is $(shell command). This returns the output of the shell function (stripping new lines).

The patsubst function has the syntax $(patsubst pattern,replacement,text). It uses the percent symbol (%) the same way pattern rules do--as a string which matches in both the pattern and the replacement text. It searches the text for whitespace-separated words that match the pattern and substitutes the replacement for them.

Makefile variables as commands, and automatic variables

# How to compile the .html and .java files.
htmlcompile=$(XMLC) -d $(class_d) -class $(subst /,.,
  $(basename $<)) $<
javacompile=javac -sourcepath . -d $(class_d) 
  $(filter %.java,$?)

These are recursively expanded variables, calculated when the variable is actually needed. The variables need to be recursively expanded because they include automatic variables that rely on prerequisite lists and will be used as command strings in rules. (Automatic variables are explained in Introduction to make.)

$< and $? are the automatic variables. $< expands to the name of the first prerequisite. $? expands to a space-separated list of prerequisites that are newer than the target.

The $(filter %.java,$?) is necessary because when javacompile is used later in a rule, one prerequisite of the rule is the directory into which the Java files are compiled. The filter removes the directory, leaving only the .java files.

Note the use of make functions in the variable definition.

Pattern rules

$(VIEWFILES): $(class_example_view_d)/%.class: 
  example/views/%.html $(class_d)
        $(htmlcompile)

$(CLASSFILES): $(class_example_d)/%.class: 
  example/%.java $(class_d)
        $(javacompile)

The first line of these rules has three parts. These are a special type of GNU make rule called static pattern rules. There is a simple pattern rule described in Introduction to make. The syntax of a static pattern rule is:

targets: target-pattern: dependency-patterns	commands

A .class file in the $(class_example_view_d) directory can be created from a corresponding .html file in the example/views/%.html directory, but only if the .class file is part of the $(VIEWFILES) list. Use static pattern rules if you have multiple ways of converting file type .A to .B and the way you choose depends on the files being converted.

The critical part of a pattern rule is the percent symbol. It refers to the same stem for both the target and the prerequisite file.

Simple rules

# A few things Make needs to know
.SUFFIXES : .html .java .class
.PHONY : clean all show_classpath

# The primary target: make all
all: $(VIEWFILES) $(CLASSFILES)

$(class_d):
        mkdir $@

show_classpath:
        @echo Here is the CLASSPATH passed to javac:
        @echo $$CLASSPATH
# note the $$ expansion

clean:
        -rm -rf $(class_d)

The all target relies on the pattern rules. The other rules are simple rules in the format:

target: prerequisites
	command

Simple rules and phony rules are both described in the article Introduction to make.

Caveats and gotchas

  • The techniques described in this article are for GNU make. make programs found on other Unix systems may not offer all of these features.
  • make rules require a tab at the beginning of each command. A series of spaces doesn't work.
  • make works backwards from the target to the prerequisites.
  • make works better in languages in which the compiler does not try to resolve dependencies itself.

Final words

This makefile demonstrates many of make's advanced features. When writing your own makefiles, start with something simple and add new features one at a time.

make can be used anywhere one file needs to be generated from another. Try experimenting with using make for configurations and update scripts, as well as for software development.

Further reading

  • man make
  • info make
  • The University of Utah's document on makefile conventions
  • The University of Hawaii's makefile tutorial
  • Use the keyword "makefile" when running a search on the Web.

Jennifer Vesperman is the author of Essential CVS. She writes for the O'Reilly Network, the Linux Documentation Project, and occasionally Linux.Com.


Return to the Linux DevCenter.




Tagged Articles

Be the first to post this article to del.icio.us

Sponsored Resources

  • Inside Lightroom
Advertisement

Sponsored by:

O'Reilly Media

©2009, O'Reilly Media, Inc.
(707) 827-7000 / (800) 998-9938
All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.
About O'Reilly
Academic Solutions
Authors
Contacts
Customer Service
Jobs
Newsletters
O'Reilly Labs
Press Room
Privacy Policy
RSS Feeds
Terms of Service
User Groups
Writing for O'Reilly
Content Archive
Business Technology
Computer Technology
Google
Microsoft
Mobile
Network
Operating System
Digital Photography
Programming
Software
Web
Web Design
More O'Reilly Sites
O'Reilly Radar
Ignite
Tools of Change for Publishing
Digital Media
Inside iPhone
O'Reilly FYI
makezine.com
craftzine.com
hackszine.com
perl.com
xml.com

Partner Sites
InsideRIA
java.net
O'Reilly Insights on Forbes.com