1

I'm using GNU makefiles to build a C project. I want to keep all build artifacts on an isolated build tree in order to minimize clutter. The project looks like this:

$prefix/
    include/$tree/program.h
    source/$tree/program.c
    build/
        objects/$tree/program.o
        dependencies/$tree/program.d

Where $prefix represents the directory of the project and $tree represents an arbitrary folder structure.

I wanted to match source files in the source/ directory to their object and dependency file counterparts in the build/ tree. So, I wrote the following rules:

# Remove the built-in rules
%.o : %.c

$(objects_directory)/%.o : $(source_directory)/%.c $(dependencies_directory)/%.d
    $(compiler_command_line) $(compiler_option_output) $@ $<

$(build_directory)/$(target) : $(objects)
    $(compiler_command_line) $(compiler_option_output) $@ $^

Make correctly figures out the compilation target and the object files needed to build it. However, make stops at this point with the error:

No rule to make target 'build/objects/project/program.o', needed by 'build/program.dll'.

So why is this happening, and how do I fix it?

I investigated the problem by running make --print-data-base, the output of which included:

# Not a target:
build/objects/project/program.o:
#  Implicit rule search has been done.
#  File does not exist.
#  File has not been updated.

Which suggests that the prerequisite is not matching the implicit rule as intended. However, I verified that it does match when I tried to work my way backwards by writing:

object := build/objects/project/program.o
$(object:$(objects_directory)/%.o=$(source_directory)/%.c)
$(object:$(objects_directory)/%.o=%)

These lines result in source/project/program.c and project/program, which means the stem is being correctly computed.

I have studied the GNU make documentation and I don't remember reading anything that suggests that this kind of pattern matching can't happen in implicit rule definitions.


Here are the variable definitions:

include_directory := include
source_directory := source

build_directory := build
objects_directory := $(build_directory)/objects
dependencies_directory := $(build_directory)/dependencies

sources := $(wildcard $(source_directory)/**/*.c)
objects := $(sources:$(source_directory)/%.c=$(objects_directory)/%.o)

# Take the name of the project's directory
target := $(notdir $(CURDIR)).dll

compiler_dependency_file = $(patsubst $(source_directory)/%.c,$(dependencies_directory)/%.d,$<)
compiler_options = -I $(include_directory) -MMD -MF $(compiler_dependency_file)

CC = gcc
compiler_command_line = $(CC) $(compiler_options) $(CFLAGS)
compiler_option_output = -o
Matheus Moreira
  • 17,106
  • 3
  • 68
  • 107

1 Answers1

1

It turns out it wasn't the pattern matching. The root of the problem was in the dependency prerequisite of the implicit rule.

The dependency file isn't supposed to be a prerequisite in the first place; it should be one of the targets that gets generated along with the object file.

As I read once more § 4.14 Generating Prerequisites Automatically of the manual, the answer jumped out at me:

The purpose of the sed command is to translate (for example):

main.o : main.c defs.h

into:

main.o main.d : main.c defs.h

While my build system makes no use of sed, the fact that main.d was on the left-hand side of the example rule felt strange. In my code, it was on the right-hand side.

When I put my rule in the left-hand side, it worked and the problem was solved. The erroneous recipe was essentially treating one of its byproducts as a prerequisite.

Matheus Moreira
  • 17,106
  • 3
  • 68
  • 107
  • `.d` are not used by the linker (or compiler). They are compiler-generated makefiles, to be included by the parent makefile, defining the dependencies of `foo.o` upon headers that would otherwise not be known as prerequisites. They prompt *make* to re-*compile* `foo.o` when it is out of date with respect to any header included by `foo.c|cpp`. – Mike Kinghan Feb 27 '16 at 10:00
  • See [this](http://stackoverflow.com/questions/8025766/makefile-auto-dependency-generation) [this](http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/) and [this](http://www.microhowto.info/howto/automatically_generate_makefile_dependencies.html) – Mike Kinghan Feb 27 '16 at 10:00
  • @MikeKinghan, I don't think I understand this process as well as I would like. I was under the impression that both object files and libraries had to be linked in a specific order, according to the dependencies between them. I think this is the problem [`pkg-config`](https://www.freedesktop.org/wiki/Software/pkg-config/) aims to solve. Is this not correct? If not, can you please clarify; I'll edit the answer. – Matheus Moreira Feb 27 '16 at 14:56
  • 1
    Object files and libraries need to be linked in such an order, that any of them which *requires* a symbol definition occurs before the one that *provides* the symbol definition - *in the linker commandline*. These require/provide relations are linkage dependencies but are not at all the same as `make` dependencies, which are relations of the form: if X is newer than Y, then do Z. `.d` files express `make` dependencies: the linker knows nothing of them. The order in which things are linked is entirely independent of the order in which they are made by `make`. – Mike Kinghan Feb 27 '16 at 15:33