Celelibi
Celelibi

Reputation: 1501

How to rebuild when the recipe has changed

I apollogize if this question has already been asked. It's not easy to search.

make has been designed with the assumption that the Makefile is kinda god-like. It is all-knowing about the future of your project and will never need any modification beside adding new source files. Which is obviously not true.

I used to make all my targets in a Makefile depend on the Makefile itself. So that if I change anything in the Makefile, the whole project is rebuilt.

This has two main limitations :

  1. It rebuilds too often. Adding a linker option or a new source file rebuilds everything.
  2. It won't rebuild if I pass a variable on the command line, like make CFLAGS=-O3.

I see a few ways of doing it correctly, but none of them seems satisfactory at first glance.

  1. Make every target depend on a file that contains the content of the recipe.
  2. Generate the whole rule with its recipe into a file destined to be included from the Makefile.
  3. Conditionally add a dependency to the targets to force them being rebuilt whenever necessary.
  4. Use the eval function to generate the rules.

But all these solutions need an uncommon way of writing the recipes. Either putting the whole rule as a string in a variable, or wrap the recipes in a function that would do some magic.

What I'm looking for is a solution to write the rules in a way as straightforward as possible. With as little additional junk as possible. How do people usually do this?

Upvotes: 6

Views: 2328

Answers (2)

George Valkov
George Valkov

Reputation: 1459

I have projects that compile for multiple platforms. When building a single project which had previously been compiled for a different architecture, one can force a rebuild manually. However when compiling all projects for OpenWRT, manual cleanup is unmanageable.

My solution was to create a marker identifying the platform. If missing, everything will recompile.

ARCH ?= $(shell uname -m)
CROSS ?= $(shell uname -s).$(ARCH)

# marker for the last built architecture
BUILT_MARKER := out/$(CROSS).built

$(BUILT_MARKER) :
    @-rm -f out/*.built
    @touch $(BUILT_MARKER)

build: $(BUILT_MARKER)
# TODO: add your build commands here

If your flags are too long, you may reduce them to a checksum.

Upvotes: 3

Vroomfondel
Vroomfondel

Reputation: 2898

"make has been designed with the assumption that the Makefile is kinda god-like. It is all-knowing about the future of your project and will never need any modification beside adding new source files."

I disagree. make was designed in a time when having your source tree sitting in a hierarchical file system was about all you needed to know about Software configuration management, and it took this idea to the logical consequence, namely that all that is, is a file (with a timestamp). So, having linker options, locator tables, compiler flags and everything else but the kitchen sink in a file, and putting the dependencies thereof also in a file, will yield a consistent, complete and error-free build environment as far as make is concerned.

This means that passing data to a process (which is nothing else than saying that this process is dependent on that data) has to be done via a file - command line arguments as make variables are an abuse of makes capabilities and lead to erroneous results. make clean is the technical remedy for a systemic misbehaviour. It wouldn't be necessary, had the software engineer designed the make process properly and correctly.

The problem is that a clean build process is hard to design and maintain. BUT: in a modern software process, transient/volatile build parameters such as make all CFLAGS=O3 never have a place anyway, as they wreck all good foundations of config management.

The only thing that can be criticised about make may be that it isn't the be-all-end-all solution to software building. I question if a program with this task would have reached even one percent of makes popularity.

TL;DR

place your compiler/linker/locator options into separate files (at a central, prominent, easy to maintain and understand, logical location), decide about the level of control through the granularity of Information (e.g. Compiler flags in one file, linker flags in another) and put the true dependencies down for all files, and voila, you will have the exactly necessary amount of compilation and a correct build.

Upvotes: 0

Related Questions