57

At work we use a common makefile that other makefiles include (via the include statement) and it has a generic "clean" target that kills some common files. I want to add on to that target in my new makefile so I can delete some specific files, but if I add a clean target in my makefile, it just overrides the old one.

I know I can just make a new target with a new name and have it call clean, and then do other stuff, but for sake of consistency I'd like to be able to just call make clean and have it do everything.

Is that possible?

Paul D.
  • 1,785
  • 2
  • 19
  • 25

8 Answers8

84

I've seen this done at several shops. The most common approach is to use double-colon rules, assuming you're using something like GNU make. In your common makefile you would have something like this:

clean::
        # standard cleanup, like remove all .o's:
        rm -f *.o

Note that there are two colons following clean, not just one!

In your other makefile you just declare clean again, as a double-colon rule:

clean::
        # custom cleanup, like remove my special generated files:
        rm -f *.h.gen

When you invoke make clean, GNU make will automagically run both of these "branches" of the clean rule:

% make clean
rm -f *.o
rm -f *.h.gen

It's simple to set up and it composes quite neatly I think. Note that specifically because it is a double-colon rule, you don't get the "overriding commands" errors you normally get when you define two rules for the same target. That's sort of the point of double-colon rules.

Javad
  • 313
  • 3
  • 13
Eric Melski
  • 16,432
  • 3
  • 38
  • 52
  • Thanks Eric, this works too, but I'd rather avoid modifying the common makefile if I can. – Paul D. Oct 30 '09 at 17:53
  • 2
    i was just looking for that and in my case the double-colon is the perfect solution. many thanks! – aurora Dec 16 '09 at 13:00
43

You can write your own clean and make it a preq of the common clean.

clean: myclean

myclean:
    rm whatever

Yours will run first. If for some reason you want the common clean to run first then the solution will be more complicated.

EDIT:

Here is the best solution I can see which runs the common rule before the local one:

include Makefile.common

clean:
    $(MAKE) -f Makefile.common $@
    rm whatever additional things

The include directive is necessary because the local makefile relies on the common one for things other than clean. The local clean rule overrides the common clean rule, but invokes the common clean rule before doing the additional work. (This overriding will cause some warnings, which is a nuisance; I don't know a good way to silence them.)

Beta
  • 96,650
  • 16
  • 149
  • 150
  • 1
    Seems like that would be a bit backwards, requiring adding more and more preq to the clean target of the common makefile. Naming the clean target something different in the common makefile and then making that target a preq of the clean target in other makefiles which include the common one seems like it would both be easier to maintain AND would prevent the common makefile from ever having a preq which disappears when a subproject goes away. Making clean a double-colon target is another clean and predictable way to solve this issue. – Dave Rawks Mar 17 '11 at 23:32
  • 4
    @Dave Rawks, you misunderstand, what I wrote would go into the *local* makefile, not the common makefile. Also, having two target names is what the OP said he didn't want. The double-colon idea is clever, although it would involve modifying the common makefile (and would not allow local makefiles to override the common rule). – Beta Mar 18 '11 at 05:21
  • Thank you, I did not realize this relation before. – Bohdan May 15 '14 at 23:52
  • What if I would like 'myclean' to run After clean? – Aleksander Fular Mar 11 '16 at 09:22
  • @AleksanderFular: Then, as I said, the solution will be more complicated. Do you have a real reason for this, or are you simply trying to improve your Make skills? – Beta Mar 13 '16 at 16:33
  • @Beta I have been trying to add to the 'help' target in the ESP32 sdk make framework. Could you please show your solution for reversing the order so I can have my \@echo output appear last? TIA – TheMadsen Nov 04 '19 at 07:11
7

Use implicit rules:

existing-target: my-extention

my-extention:
    echo running command 1
    echo running command 2

Very simple make tutorial to ramp up.

When using :: you can run into issues since make complains when you mix single colon : and double colon :: rules:

a:
    echo a

a::
    echo aa

will result in:

. . .
*** target file `a' has both : and :: entries.  Stop.
Bohdan
  • 16,531
  • 16
  • 74
  • 68
6

It seems like the common makefile's rule should be called something like common-clean. Then each main makefile would declare their clean rule as

clean: common-clean

and you're set.

If that isn't an option, you could take a look at double colon rules, but those introduce a whole other set of issues to consider.

jamessan
  • 41,569
  • 8
  • 85
  • 85
3

Adding another possible solution I've seen for posterity... I know the OP was wary about changing the common makefile, but something like this works and involves minimal changes.

local makefile 1:

CLEAN=MyExe1 MyExe2
....
include /my/common/makefile

local makefile 2:

CLEAN=MyExe3 MyExe4
....
include /my/common/makefile

common makefile:

clean:
     rm -f *.dep *.o *.a $(CLEAN)

Basically the idea is to define some variable (in this case CLEAN) in each local makefile with all the specific items you want to delete. Then the common makefile runs rm -f on all the common file types to delete, plus whatever was specifically flagged for deletion in each local makefile via the CLEAN variable. If there's nothing specific to delete, simply omit the variable declaration or leave it empty (CLEAN=)

So now if we run make clean for local makefile 1, it executes

rm -f *.dep *.o *.a MyExe1 MyExe2

And if we run make clean for local makefile 2, it executes

rm -f *.dep *.o *.a MyExe3 MyExe4
yano
  • 4,827
  • 2
  • 23
  • 35
  • 1
    This is the starting base for a good generallised clean solution. You can also use the += operator if you need to execute both makefile 1 and 2 at the same time. – Johan Boulé Aug 15 '19 at 12:37
2

I've found a better solution:

.PHONY: my-extra-clean

clean: my-extra-clean

my-extra-clean:
      rm <whatever-you-want>

include Makefile.common

The key line is clean: my-extra-clean. Ie, you can add dependencies in separate stanzas in different makefiles to add behaviour. my-extra-clean is run as a dependency of the root clean target.

artless noise
  • 21,212
  • 6
  • 68
  • 105
1

For ours, we define a variable, EXTRAFILESTOCLEAN, then when the clean rule runs, it has a step to remove anything specified in the EXTRAFILESTOCLEAN variable

clean:
    rm -f *.o
ifdef $(EXTRAFILESTOCLEAN)
    rm -f $(EXTRAFILESTOCLEAN)
endif

That can cause unexpected problems if you set that variable to weird values, but you could guard against those by adding prefixes or other tests.

davenpcj
  • 12,508
  • 5
  • 40
  • 37
  • I think i would remove the `ifdef` and just put the variable directly on the first `rm` command, which would not change anything if the variable expands to nothing. I'm saying this because I can't remember on top of my head *when* `ifdef` is evaluated. I suspect it might not be when the rule is executed but rather just when the makefile is parsed. So if your variable is only set later on, the `ifdef` will not work. – Johan Boulé Aug 15 '19 at 12:42
1

It's in the docs: https://www.gnu.org/software/make/manual/html_node/Overriding-Makefiles.html

So instead of include Makefile you use a wildcard target and forward it to the base Makefile:

# -include base.Makefile <--- not this

%:
    @$(MAKE) -f base.Makefile $@
Clintm
  • 4,505
  • 3
  • 41
  • 54