Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Makefile: depend on every file of a directory

I'd like to do a Makefile that runs either with gnumake or makepp that packs all the files under given directiories:

DIRS:=$(shell find . -mindepth 2 -maxdepth 2 -not -name mp3 -not -name ".*" -type d)
PACKAGES = $(DIRS:%=%.npk)

all: packages

packages: $(PACKAGES)

%.npk: %/*
    npack c $@ @^

.PHONY: all packages

the problem is that there's no such thing as %/* in the dependencies. I need the targets (X.npk) to depend on every file in directory X, but I don't know what the files are when I write the Makefile, 'cause they're generated later.

An example:

./dirA/x
./dirA/y
./dirB/e
./dirB/f

I'd like to create ./dirA.npk (depending on x,y), ./dirB.npk (e,f) There's nothing I know about the dirs or the files in advance except that the find used in the 1st line finds all the dirs.

like image 704
Gavriel Avatar asked Apr 13 '11 23:04

Gavriel


People also ask

What is a makefile dependency?

A dependency is a file that is used as input to create the target. A target often depends on several files. A command is an action that make carries out. A rule may have more than one command, each on its own line.

Can makefile target be a directory?

Yes, a Makefile can have a directory as target. However, you shouldn't. When a file is added or removed from a directory, its mtime is updated. This can cause weird behaviour by causing multiple targets depending on a single directory to become implicitly rebuilt by one another.

Does the order of Makefiles matter?

The order of rules is not significant, except for determining the default goal: the target for make to consider, if you do not otherwise specify one. The default goal is the target of the first rule in the first makefile. If the first rule has multiple targets, only the first target is taken as the default.

What is all rule in makefile?

This means that when you do a "make all", make always thinks that it needs to build it, and so executes all the commands for that target. Those commands will typically be ones that build all the end-products that the makefile knows about, but it could do anything.


1 Answers

Try using the wildcard directive:

DEPS := $(foreach dir, $(DIRS), $(wildcard $(dir)/*))

%.npk: $(DEPS)
    npack c $@ $^

EDIT: The above is just an example of using wildcard and makes each .npk file dependent on the files in all of the other folders. Your usage would be slightly different.

I think there may be an easier way to go about this. Why are you wanting to have a dependency on all of the files in the folder? Is it just to use the $^ operator? Or do you need to rebuild the .npk if any of the files changed?

One alternate (and possibly cleaner) solution would be to use the find utility in your recipe instead of $^ and use the .FORCE directive to always force the .npk file to be rebuilt. The downside is that .npk files may be rebuilt unnecessarily.

EDIT 2: If there's not a way to do this cleanly with make commands, you can work around it by using .FORCE to ensure that the recipe is always run and move the "should I rebuild this file" check into the body of the recipe:

%.npk: .FORCE
    check_for_rebuild.sh $@ && npack c $@ $^

where check_for_rebuild.sh is a shell script that does something like this:

#!/bin/bash
# Returns non-zero if the archive needs to be rebuilt
if [ -e $1 ]; then
    folder_name=$(basename $1 .npk)
    [ -z "$(find $folder_name -newer $1 -not -type d)" ] && return 0
fi
return 1

I don't really like that solution because it works around the problem instead of solving it directly, but it may be able to get you going in the meantime. If you are going to go that route, it's probably cleaner and easier to do everything in the shell script and either have the makefile simply invoke the script or get rid of the makefile entirely.

like image 173
bta Avatar answered Nov 07 '22 15:11

bta