4
\$\begingroup\$

This is a completely refactored code of Makefile for a tiny C++ project based on all of the information in answers there, also comments, and a bit of my common sense.

I did my best, I worked really hard on it, read twice every comment, every bit of the answers, and also I read some of the documentation.

Now, I appear to reach the end of the road, without further response from you, that is. I have re-written it from scratch.

I forgot to copy from the original question, that the solution must be portable, e.g. $(PROGRAM).o: CXXFLAGS += -g is not portable, I tried it with BSD make, it errored out, unfortunatelly. So, in essence, I forgot to include also in here, that the Makefile must be portable = BSD make usable. Thank you, and late apology.

If you do not explicitly want, you do not need to read the original question at all. If you haven't already, that is.


# Copyright 2024 Vlastimil Burian under MIT license
# This is the project's Makefile version 0.1 alpha!
# Bug reports email: [email protected]

CXX = g++
CXXFLAGS = -std=c++11 -O3 -Wall -Wextra -Werror -Wpedantic -pedantic-errors
PROGRAM = fan-control

.PHONY: all debug run distrib clean install uninstall

all: $(PROGRAM)

$(PROGRAM).o: $(PROGRAM).cpp
    $(CXX) -c -g $(CXXFLAGS) $(PROGRAM).cpp -o $(PROGRAM).o

$(PROGRAM): $(PROGRAM).o
    $(CXX) $(CXXFLAGS) $(PROGRAM).o -o $(PROGRAM)
    strip -s $(PROGRAM)

debug: $(PROGRAM).o
    $(CXX) -g $(CXXFLAGS) $(PROGRAM).o -o $(PROGRAM)

run: $(PROGRAM)
    ./$(PROGRAM)

distrib: $(PROGRAM).cpp Makefile
    tar -H posix -czf $(PROGRAM).tar.gz $(PROGRAM).cpp Makefile

clean:
    rm -fv *.o $(PROGRAM) $(PROGRAM).tar.gz

install:
    @if [ ! -f $(PROGRAM) ]; then printf '%s\n' 'You need to run either `make`, or `make debug` first.' && exit 1; fi
    @if [ ! `id -u` -eq 0 ]; then printf '%s\n' 'You need to run `install` target either with `sudo`, or as root.' && exit 1; fi
    @if [ ! -d /usr/local/bin ]; then printf '%s\n' 'This program wanted to install into /usr/local/bin, but that directory does not exist on this system.' && exit 1; fi
    cp -v -i $(PROGRAM) /usr/local/bin/

uninstall:
    @if [ ! -f /usr/local/bin/$(PROGRAM) ]; then printf '%s\n' '/usr/local/bin/$(PROGRAM) is not installed.' && exit 1; fi
    @if [ ! `id -u` -eq 0 ]; then printf '%s\n' 'You need to run `uninstall` target either with `sudo`, or as root.' && exit 1; fi
    rm -v /usr/local/bin/$(PROGRAM)
\$\endgroup\$

1 Answer 1

7
\$\begingroup\$

Consider this rule, which almost duplicates the built-in commands:

$(PROGRAM).o: $(PROGRAM).cpp
    $(CXX) -c -g $(CXXFLAGS) $(PROGRAM).cpp -o $(PROGRAM).o

The way to get minor deviations from standard rules is to provide target-specific variables:

$(PROGRAM).o: CXXFLAGS += -g

I think it's dangerous that this rule creates a file that's named by a different target:

debug: $(PROGRAM).o
    $(CXX) -g $(CXXFLAGS) $(PROGRAM).o -o $(PROGRAM)

If we make debug then make all, well get a different build than if we make from clean. That's undesirable, to say the least!


There's some problems with the install target:

    @if [ ! -f $(PROGRAM) ]; then printf '%s\n' 'You need to run either `make`, or `make debug` first.' && exit 1; fi

It's probably better to just make install depend on $(PROGRAM).

    @if [ ! `id -u` -eq 0 ]; then printf '%s\n' 'You need to run `install` target either with `sudo`, or as root.' && exit 1; fi

This test seems over-constrained. Why shouldn't other users be allowed this target, provided that they are allow to write to the installation directory? Let anyone attempt this, rather than implementing an inaccurate check.

    @if [ ! -d /usr/local/bin ]; then printf '%s\n' 'This program wanted to install into /usr/local/bin, but that directory does not exist on this system.' && exit 1; fi

Again, this is a test that is redundant, as the command we're about to use can perform this check.

    cp -v -i $(PROGRAM) /usr/local/bin/

Why not use the install program for this? That's exactly the job it was created for!


All Makefiles should have a .DELETE_ON_ERROR: magic target. Get in the habit of writing that as soon as you create the file.

\$\endgroup\$
5
  • \$\begingroup\$ What exactly is undesirable on the debug target? My thought was to make it include debug symbols, which is why they differ obviously. Should I rename it to something more appropriate? Or do something else with it? \$\endgroup\$ Commented Dec 23, 2024 at 11:35
  • \$\begingroup\$ @VlastimilBurián I think the debug target as such is fine, but it should not be a prerequisite for the production build. I.e., after make debug, which works fine, make fan-control is broken because it produces, inadvertently, a debug build (the debug-build fan-control.o is still around, is not stale and does not get rebuilt). \$\endgroup\$ Commented Dec 23, 2024 at 11:50
  • \$\begingroup\$ Which is, of course, a consequence of replicating all the nice built-in rules ;-). That's why Toby says correctly: The debug target should do nothing but set flags in the CXXFLAGS variable, and leave the actual work to the existing rules. \$\endgroup\$ Commented Dec 23, 2024 at 11:51
  • \$\begingroup\$ @Peter-ReinstateMonica These are sections, I did not manage to separate, to be honest. Thanks for any directions. \$\endgroup\$ Commented Dec 23, 2024 at 12:03
  • \$\begingroup\$ @Vlastimil, as Peter says, the fan-control target is built from fan-control.o, which is produced by two different targets. So when you make fan-control, you don't know which object file you're linking into it. The usual solution is to have two build directories sharing the same Makefile (use the VPATH variable to tell Make where to find the sources) but with different environment variables. Or you can use separate Makefiles that specify the different flags and VPATH then include the common Makefile. \$\endgroup\$ Commented Dec 23, 2024 at 13:21

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.