Sunday, April 15, 2007

Implementing Non-Recursive Make

I had to take over the web application piece of a project that I worked on after it became abandonware from the software vendor. I needed a build system for this project and decided to implement it using non-recursive make having previously read Peter Miller's paper. I based mine on the non-recursive make system presented by Emile van Bergen, but I ran into several problems with his approach. These are my notes on how I resolved my problems.

Documentation and Suggested Reading


Length Limit of Macros


The original presentation for implementing non-recursive make had individual macros defined to contain all of the source or object files. However, due to the sheer number of files in the webapp project, almost 2,000 after code generation, the upper bound for the length of a macro was exceeded. To resolve this problem my Makefile defines macros to hold the names of directory targets. Where each directory target is dependent upon the contents of per-directory macros, which contain the source or object files for that directory.

For example the Makefile contains:

...
CALLENTRY := $(DIR_LIBS)/fbCallEntry.jar
CALLENTRY_TGT :=
...
dir := com/fb/callentry
include $(dir)/Rules.mk
...

$(CALLENTRY) : $(COMMON) $(CALLENTRY_TGT)
@echo
...

The Rules.mk files in the callentry sub directories contain something like:

...
d := $(dir)
CALLENTRY_TGT += $(d)
...
CALLENTRY_SRCS_$(d) := \
$(d)/CallEntryObjectRegistry.java \
$(d)/CallHistoryItem.java \
$(d)/Customer.java

CALLENTRY_OBJ_$(d) := $(CALLENTRY_SRCS_$(d):%.java=%.class)
...

$(d) : $(CALLENTRY_OBJ_$(d))
$(d)/Rules.mk

CALLENTRY_OBJ_$(d) : CALLENTRY_SRCS_$(d)

Clean Targets


One of the goals was to keep the directory level details in the directory's Rules.mk file. However, this makes it difficult to implement directory level clean targets. The difficulty arises in that you no longer know the directory path after the rules.mk file has been loaded. The solution to this problem was to encode the directory path into the per-directory clean target, and then extract the directory path by using make's substitution rules.

For example the Makefile contains:

...
CLN_TGT :=
REAL_CLN_TGT :=

DIST_CLN_TGT:=
...

clean :$(CLN_TGT)

realclean : clean $(REAL_CLN_TGT)

distclean : realclean $(DIST_CLEAN_TGT)
...

The Rules.mk files in the callentry subdirectories contain something like:

...
CLN_TGT += CLN_$(d)
...

CLN_$(d) :
rm -f $(subst CLN_,,$@)/*.class
...

No comments: