Can Make be made to understand that a/../z and b/../z are the same location?

58 Views Asked by At

I have a somewhat large and complex Makefile setup that postprocesses some data files. Overall it work quite well, but I have run into an annoying issue where Make builds the same target many times over under different directory names.

As a simple example, consider the Makefile

foo : 1/foo 2/foo
    cat $^ > $@

%/foo : %/../bar
    cat $^ > $@

%/bar : %/baz
    cat $^ > $@

Initialized with file baz and directories 1 and 2, make will produce the following series of builds, when done in parallel mode:

cat 1/../baz > 1/../bar
cat 2/../baz > 2/../bar
cat 2/../bar > 2/foo
cat 1/../bar > 1/foo
cat 1/foo 2/foo > foo
rm 2/../bar

Note how the first two commands do exactly the same thing.

Is there a way of convincing Make that 1/../bar and 2/../bar are the same file, so it only should do that recipe once?

1

There are 1 best solutions below

1
On BEST ANSWER

Instead of using relative path, you can use absolute path by using the realpath or abspath functions:

$(realpath names…)

For each file name in names return the canonical absolute name. A canonical name does not contain any . or .. components, nor any repeated path separators (/) or symlinks. In case of a failure the empty string is returned. Consult the realpath(3) documentation for a list of possible failure causes.

$(abspath names…)

For each file name in names return an absolute name that does not contain any . or .. components, nor any repeated path separators (/). Note that, in contrast to realpath function, abspath does not resolve symlinks and does not require the file names to refer to an existing file or directory. Use the wildcard function to test for existence.

$(abspath foo): $(abspath 1/foo) $(abspath 2/foo)
        cat $^ > $@

%/foo: $(abspath %/../bar)
        cat $^ > $@

%/bar: %/baz
        cat $^ > $@

Or something like that:

TOP := $(abspath .)

$(TOP)/foo: $(TOP)/1/foo $(TOP)/2/foo
        cat $^ > $@

%/foo: $(abspath %/../bar)
        cat $^ > $@

%/bar: %/baz
        cat $^ > $@

Here is the result

jmlemetayer@prometheus:~/foobar$ make -j5
cat /home/jmlemetayer/foobar/baz > /home/jmlemetayer/foobar/bar
cat /home/jmlemetayer/foobar/bar > /home/jmlemetayer/foobar/1/foo
cat /home/jmlemetayer/foobar/bar > /home/jmlemetayer/foobar/2/foo
cat /home/jmlemetayer/foobar/1/foo /home/jmlemetayer/foobar/2/foo > /home/jmlemetayer/foobar/foo
rm /home/jmlemetayer/foobar/bar