help-make
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Issue with order-only-prerequisites on pattern rule


From: Juan Pablo Garibotti Arias
Subject: Issue with order-only-prerequisites on pattern rule
Date: Thu, 28 May 2020 19:34:47 +0200

I've been having an issue with getting a pattern rule to correctly
read order-only-prerequisites, and wanted to check with more
experienced people before posting the issue on the bug mailing list.

The main issue is trying to configure a project for out-of-source
builds of multiple configurations. So, let's say the project directory
has the following structure:

src/foo/main.cpp
Makefile

And I want make to use build/debug/foo/ as the intermediate debug
location, build/release/foo/ as the release intermediate location, and
bin/debug/ and bin/release for the binary output. So, after running
the debug build, I should be left with:

bin/debug/foo
build/debug/foo/main.o
src/foo/main.cpp
Makefile

I've written a Makefile like so:
# Makefile #
config := debug
build_dir := build/$(config)/
bin_dir := bin/$(config)/

sources := foo/main.cpp
objects := $(patsubst %.cpp,$(build_dir)%.o,$(sources))

.PHONY : foo
foo : $(bin_dir)foo

%/ :
    mkdir -p $@

$(bin_dir)foo : $(objects) | $(bin_dir)
    $(CXX) $< -o $@

$(build_dir)%.o : src/%.cpp | $(dir $(build_dir)%)
    $(CXX) $< -c -o $@

.PHONY : clean
clean :
    rm -rf bin/ build/

# end of Makefile #

I've simplified for demonstration purposes, assume that setting the
config variable changes compiler flags and such. Also, I'm using
spaces because the mail client isn't showing tabs, but the rule recipe
would be indented with a tab.

The problem I have is that the % sign on the order-only-prerequisite
on the object pattern rule doesn't work. What I expect to happen is
that the phony target foo depends on $(bin_dir)foo, which depends on
$(objects) which resolves to build/debug/foo/main.o, which triggers
the pattern rule for $(build_dir)%.o, with % matching "foo/main". This
then depends on src/foo/main.cpp, with $(dir build/debug/foo/main)
resulting in build/debug/foo/ as an order only requirement. The
directory then gets built by the %/ pattern rule, then then object is
compiled, the binary linked, and make is done.

What happens, however, is that the % in the $(build_dir)%.o pattern
rule, in the order-only-prerequisite, is replaced with nothing,
resulting in $(dir build/debug/), the build directory is made without
the module subdirectory, and then the compilation fails because the
compiler can't output to the nonexistent foo subdirectory. This is a
sample output of the above situation, with --debug enabled (tested
with the latest source release at
http://ftp.gnu.org/gnu/make/make-4.3.tar.gz):

$ make --debug
GNU Make 4.3
Built for x86_64-pc-linux-gnu
Copyright (C) 1988-2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Reading makefiles...
Updating makefiles....
Updating goal targets....
 File 'foo' does not exist.
   File 'bin/debug/foo' does not exist.
     File 'build/debug/foo/main.o' does not exist.
      File 'build/debug/' does not exist.
     Must remake target 'build/debug/'.
mkdir -p build/debug/
     Successfully remade target file 'build/debug/'.
    Must remake target 'build/debug/foo/main.o'.
g++ src/foo/main.cpp -c -o build/debug/foo/main.o
Assembler messages:
Fatal error: can't create build/debug/foo/main.o: No such file or directory
make: *** [Makefile:19: build/debug/foo/main.o] Error 1
Removing intermediate files...
rm build/debug/make: unlink: build/debug/: Is a directory

The issue lies in the "      File 'build/debug/' does not exist."
line. I played around with it, and noticed that if I add a slash after
the %, the pattern is expanded. The problem then is that the slash
prevents the dir function from removing the file name from the
prerequisite, causing unnecessary directories to be created (one per
target object file). So if I write the rule as

(build_dir)%.o : src/%.cpp | $(dir $(build_dir)%/)

instead of build/debug/foo/ being created, I get
build/debug/foo/main/. I tried putting other punctuation there, but it
only gets correctly replaced with the slash, which is the only
character that prevents dir from doing what I want it to do.

I have a workaround for this, of course. Adding the rule

$(objects): | $(sort $(dir $(objects)))

makes every object depend on every build directory, so all my build
directories get properly built. But that's not correct, as the
dependency graph ends up with redundant dependencies. It works, but it
annoys me.

So, is my expectation correct and is this a bug in make, or have I
misunderstood something about how pattern rules and
order-only-prerequisites interact? Should I send this to the bug
mailing list instead?

If you read this far, thank you for your time. Regards,
Juan Pablo



reply via email to

[Prev in Thread] Current Thread [Next in Thread]