7

I have several widgets denoted by a config.xml in their root in a directory layout.

The GNUmakefile I have here is able to build them. Though if I update the folders, the dependencies aren't tracked. I don't want to depend on a clean target obviously, so how do I track the contents of each folder?

WGTS := $(shell find -name 'config.xml' | while read wgtdir; do echo `dirname $$wgtdir`.wgt; done )
all: $(WGTS)
%.wgt: 
    @cd $* && zip -q -r ../$(shell basename $*).wgt .
    @echo Created $@
clean:
     rm -f $(WGTS)

I hoped something like:

 %.wgt: $(shell find $* -type f)

Would work, but it doesn't. Help.

hendry
  • 9,725
  • 18
  • 81
  • 139

2 Answers2

4

Combining Beta's idea with mine:

WGTS := $(shell find -name config.xml)
WGTS := $(WGTS:/config.xml=.wgt)
WGTS_d := $(WGTS:.wgt=.wgt.d)

all: $(WGTS)
clean:
    rm -f $(WGTS) $(WGTS_d)

-include $(WGTS_d)

define WGT_RULE
$(1): $(shell find $(1:.wgt=))
$(1:.wgt=)/%:
    @
endef
$(foreach targ,$(WGTS),$(eval $(call WGT_RULE,$(targ))))
%.wgt:
    @echo Creating $@
    @(echo -n "$@: "; find $* -type f | tr '\n' ' ') > $@.d
    @cd $* && zip -q -r ../$(shell basename $*).wgt .

Example:

$ mkdir -p foo bar/nested
$ touch {foo,bar/nested}/config.xml
$ make
Creating bar/nested.wgt
Creating foo.wgt
$ make
make: Nothing to be done for `all'.
$ touch foo/a
$ make
Creating foo.wgt
$ rm foo/a
$ make
Creating foo.wgt
$ make
make: Nothing to be done for `all'.

The only potential problem here is the dummy rule that lets make ignore targets it doesn't know how to build which are nested inside the directories. (foo/a in my example.) If those are real targets that make needs to know how to build, the duplicate recipe definition may be a problem.

Community
  • 1
  • 1
Thomas Edleson
  • 2,175
  • 1
  • 16
  • 19
  • I need it to account for the new files added. I don't quite understand the need to write out that .wgt.d thing, it looks like cruft to me. There won't be a case with config.xml inside a config.xml. :) – hendry May 22 '11 at 13:22
  • @hendry: Writing out the dependency files lets make simply include them on subsequent runs without having to regenerate all of that info. It does work well for most cases (and is commonly used to generate C-source dependencies on headers); but it just can't handle adding files with targets that only depend on filesystem state. – Thomas Edleson May 22 '11 at 14:41
  • @thomas This Makefile can account for adding as well as removing files, however I can't adapt it to account for the arbitrary directory structure my widgets are in http://tests.wacapps.net/?p=wac2tests;a=blob;f=samples/Makefile; I think if we understood http://www.gnu.org/s/hello/manual/make/Secondary-Expansion.html we would find the answer – hendry May 22 '11 at 15:24
  • @hendry: That makefile cannot know if a file, e.g. foo/a, has been removed since the last time foo.wgt was built. The .SECONDEXPANSION does not affect the issue of removing files one way or the other. – Thomas Edleson May 22 '11 at 16:00
  • Make seriously can't track when a file has been removed? That's a bit disappointing. Your modification to Beta's seems to work otherwise, which is great. I'll give it a couple of hours before making the award. Thanks again, – hendry May 22 '11 at 17:00
  • @hendry: Make certainly can track targets/dependencies it has been told, but the problem here is we are trying to *dynamically generate* dependencies based on the *current state* of the filesystem. If a file has been deleted between runs, we don't store anything from the previous run anywhere. My first approach, storing those .d files, would be a step in that direction. In fact, both can be combined, and I'll do that now. – Thomas Edleson May 22 '11 at 17:04
  • This is looking AWESOME thank you Thomas. Btw you are missing the first two lines. – hendry May 22 '11 at 22:47
1

Probably the best way to do this is to create the prerequisite lists explicitly, beforehand:

define WGT_RULE
$(1).wgt: $(wildcard $(1)/*)
endef

$(foreach targ,$(WGTS),$(eval $(call WGT_RULE,$(targ))))

There is another way that's very clever (a phrase that makes a good programmer wary). Years ago I came up with a left-handed kludge for treating a directory as a prerequisite. I'll see if I can dig up my old notebooks if the above isn't good enough.

EDIT:
Sorry, I didn't consider subdirectories. Here's a complete makefile (I left out the clean rule) that should do the trick.

WGTS := $(shell find -name 'config.xml' | while read wgtdir; do echo `dirname $\
$wgtdir`.wgt; done )
all: $(WGTS)

# This constructs a rule without commands ("foo.wgt: foo/bar.txt foo/baz.dat...").
define WGT_RULE
$(1).wgt: $(shell find $(1))
endef

# This invokes the above to create a rule for each widget.
$(foreach targ,$(WGTS),$(eval $(call WGT_RULE,$(targ))))

%.wgt:
    @cd $* && zip -q -r ../$(shell basename $*).wgt .
    @echo Created $@
Beta
  • 96,650
  • 16
  • 149
  • 150
  • I just tried creating a new file in a sub-directory and the makefile didn't pick it up. :( Is there a way to avoid this WGT_RUL? It seems a little unnecessary to me. I would prefer it to right of `%.wgt:` Sorry to be difficult... – hendry May 18 '11 at 17:01
  • This does not work: `hendry@x201 foo$ mkdir bar hendry@x201 foo$ touch bar/config.xml hendry@x201 foo$ make find: `./bar.wgt': No such file or directory Created bar.wgt hendry@x201 foo$ make make: Nothing to be done for `all'. hendry@x201 foo$ vim Makefile hendry@x201 foo$ touch bar/foo hendry@x201 foo$ make make: Nothing to be done for `all'. hendry@x201 foo$ ` – hendry May 20 '11 at 07:45