Friday, December 12, 2008

Spaces are a pain in painless non-recursive Make

In my book GNU Make Unleashed I published a pattern for doing Make without having a recursive descent into directories. It works well and I know that many people are using it.

But the other day I received an email from Terry V. Bush at VMWare saying that he had trouble with it because of 'the third-party problem'. The third-party problem is my name for the problem that occurs when your beautifully written Make system has to incorporate some wart of source code from a third-party vendor. In Terry's case that third-party has spaces in the path names.

Space is path names are a real bind in Make (that's another topic I cover in GNU Make Unleashed) and Terry really wanted to use my non-recursive Make pattern but needed to handle this ugly third-party.

I'll let him continue the story...

What happens is that if you have a directory name with a space in it your functions fail to find the root. Also, they always walk the entire tree up to the top even after they have found the root of the tree. Here is the version published in "GNU Make Unleashed":

sp :=
sp +=

_walk = $(if $1,$(wildcard /$(subst $(sp),/,$1)/$2) \
$(call _walk,$(wordlist 2,$(words $1),x $1),$2))

_find = $(firstword $(call _walk,$(strip $(subst /, ,$1)),$2))
_ROOT := $(patsubst %/root.mak,%,$(call _find,$(CURDIR),root.mak))

What I have done to solve these two issues is:

1: Add an "if" that returns when the root is found. This actually makes other parts of this function simpler. It also makes is slightly faster, albeit very slightly...

2: Substituted a "|" char (any char that is highly unlikely to be in a real directory name will work) for each space in the path and then put the spaces back when necessary.

Also, to simplify things a little, I added an eval that puts the result of wildcard into a temp var "_X" so that returning it when found is trivial.

sp :=
sp +=
_walk = $(if $1, \
$(if $(eval _X=$(wildcard /$(subst |,\$(sp),$(subst \
$(sp),/,$1))/$2))$(_X),$(_X), \
$(call _walk,$(wordlist 2,$(words $1),x $1),$2)))
_find = $(call _walk,$(strip $(subst /,$(sp),$(subst $(sp),|,$1))),$2)
_ROOT := $(patsubst %/root.mak,%,$(call _find,$(CURDIR),root.mak))

My plan for this is to combine your "Painless non-recursive Make" with Paul D. Smith's "Advanced Auto-Dependency Generation" Make code to produce a fast and extensible Make environment for products at VMware.

Nice, and "Advanced Auto-Dependency Generation" is also covered in GNU Make Unleashed.

No comments: