3
\$\begingroup\$

Directory Structure:

.
├── check.sh
├── GNUmakefile
├── Makefile
└── src
  • Makefile has these contents:

    # Dummy Makefile to indicate that GNU Make should be used.
    .POSIX:
    all x:
     @echo "You need GNU Make to build x" 1>&2; false
    

    When run on Solaris, a BSD system, or any other system that doesn't come with GNU make as the default make, this results in a nice error message. On Linux, and others that come with GNU make as the default, this file is ignored because GNU make looks for "GNUMakefile" first.

  • The src directory is empty because I haven't actually written any code yet. "check.sh" is a shell script that checks whether the program has been installed at $(PREFIX)/bin/. If it is not, it prints a message showing how to add $(PREFIX)/bin/ to the PATH variable, and then offers to add it automatically. (There's no test directory or test target because there are none yet. I will add that later.)

  • It is generic in the sense that I only have to change the value of target for other projects, because the directory structure remains the same. It should work at least on Windows (MINGW64 and CYGWIN), Linux, Oracle Solaris, OmniOS, OpenBSD, NetBSD, FreeBSD, and Mac OS X. (Assuming proper tools like gcc/clang, valgrind, lubsan et cetera are installed).

Makefile:

ifeq '$(PREFIX)' ''
 ifeq ($(shell id -u), 0)
 PREFIX := /usr/local
 else
 PREFIX := $(HOME)/.local
 endif
endif
MKDIR := mkdir
INSTALL := install -Dpm 755
RM := rm
VALGRIND := valgrind --tool=memcheck --leak-check=yes
CC := $(CC)
CFLAGS := $(CFLAGS)
CFLAGS += -std=c2x
CFLAGS += -I..
COMPILE.C = $(CC) $(CFLAGS) 
COMPILER_VERSION := $(shell $(CC) --version)
ifneq '$(findstring debug,$(MAKECMDGOALS))' ''
 CFLAGS += -g3
 CFLAGS += -ggdb
 CFLAGS += -no-pie
 CFLAGS += -fno-builtin
 CFLAGS += -fno-common
 CFLAGS += -fno-omit-frame-pointer
 CFLAGS += -Wall
 CFLAGS += -Wextra
 CFLAGS += -Warray-bounds
 CFLAGS += -Wconversion
 CFLAGS += -Wformat-signedness
 CFLAGS += -Wno-parentheses
 CFLAGS += -Wpedantic
 CFLAGS += -pedantic-errors
 CFLAGS += -Wstrict-prototypes
 CFLAGS += -Wwrite-strings
 CFLAGS += -Wno-missing-braces
 CFLAGS += -Wno-missing-field-initializers
 CFLAGS += -fsanitize=address
 CFLAGS += -fsanitize=undefined
 CFLAGS += -fsanitize=bounds-strict
 CFLAGS += -fsanitize=leak
 CFLAGS += -fsanitize=null
 CFLAGS += -fsanitize=signed-integer-overflow
 CFLAGS += -fsanitize=bool
 CFLAGS += -fsanitize=pointer-overflow
 CFLAGS += -fsanitize-address-use-after-scope
 ifneq '$(findstring clang,$(COMPILER_VERSION))' ''
 CFLAGS += -fsanitize=function
 CFLAGS += -fsanitize=implicit-unsigned-integer-truncation
 CFLAGS += -fsanitize=implicit-signed-integer-truncation
 CFLAGS += -fsanitize=implicit-integer-sign-change
 CFLAGS += -Wreserved-identifier
 CFLAGS += -Xanalyzer
 else ifneq '$(findstring gcc,$(COMPILER_VERSION))' ''
 CFLAGS += -Wformat-signedness
 CFLAGS += -Wsuggest-attribute=pure
 CFLAGS += -Wsuggest-attribute=const
 CFLAGS += -Wsuggest-attribute=noreturn
 CFLAGS += -Wsuggest-attribute=cold
 CFLAGS += -Wsuggest-attribute=malloc
 CFLAGS += -Wsuggest-attribute=format
 CFLAGS += -fanalyzer
 endif
else ifneq '$(findstring release,$(MAKECMDGOALS))' ''
 ifneq '$(findstring gcc,$(COMPILER_VERSION))' ''
 ifeq '$(firstword $(sort $(shell $(CC) -dumpversion) 14))' '14'
 CFLAGS += -fhardened
 else
 CFLAGS += -D_FORTIFY_SOURCE=2
 CFLAGS += -fPIE
 endif
 endif
 CFLAGS += -pedantic
 CFLAGS += -O3
 CFLAGS += -march=native
 CFLAGS += -s
endif
src-dir := src
srcs := $(wildcard $(src-dir)/*.c)
obj-dir := obj
objs := $(patsubst $(src-dir)/%.c,$(obj-dir)/%.o,$(srcs))
deps := $(objs:.o=.d)
target := kilo
.PHONY: release debug 
release debug: $(target)
 
$(target): $(objs) | $(obj-dir) 
 $(COMPILE.C) -o $@ $^
$(obj-dir)/%.o: CFLAGS += -MMD -c
$(obj-dir)/%.o: $(src-dir)/%.c | $(obj-dir)
 $(COMPILE.C) -o $@ $<
$(obj-dir):
 $(MKDIR) $(obj-dir)
ifneq '$(MAKECMDGOALS)' 'clean'
 -include $(deps)
endif
.PHONY: valgrind
valgrind: clean $(target)
 $(VALGRIND) ./$(target)
.PHONY: fclean
fclean:
 $(RM) $(target)
.PHONY: clean
clean:
 $(RM) -rf $(obj-dir) $(target) 
.PHONY: install
install: release
 $(INSTALL) $(target) $(PREFIX)/bin
 @./check.sh $(PREFIX)/bin $(target)
.PHONY: uninstall
uninstall:
 $(RM) -f $(PREFIX)/bin/$(target)
.DELETE_ON_ERROR:

Review Request:

Redundancies, duplication, simplifications. Anything. Everything.

asked Jun 27, 2024 at 10:00
\$\endgroup\$
4
  • \$\begingroup\$ CC := $(CC) is a null op. \$\endgroup\$ Commented Jun 27, 2024 at 19:14
  • \$\begingroup\$ @CrisLuengo Does it not change it from a recursively expanded variable to a simply expanded variable? \$\endgroup\$ Commented Jun 27, 2024 at 20:00
  • \$\begingroup\$ Does that matter? $(CC) will still produce the same value it did before that statement. Was CC even defined in terms of other variables? \$\endgroup\$ Commented Jun 27, 2024 at 20:13
  • \$\begingroup\$ Not for $(CC) really. Just saw it being suggested on some StackOverflow answers. \$\endgroup\$ Commented Jun 28, 2024 at 4:34

1 Answer 1

5
\$\begingroup\$

This looks wrong:

$(target): $(objs) | $(obj-dir) 
 $(COMPILE.C) -o $@ $^

Make predefines $(LINK.c) for this command.

answered Jun 27, 2024 at 10:13
\$\endgroup\$
4
  • \$\begingroup\$ Only now did I discover $(COMPILE.c) and $(LINK.c), thanks! Do you think there would be a problem due to keeping the object files of both the debug and release build in the same directory? \$\endgroup\$ Commented Jun 27, 2024 at 11:18
  • 1
    \$\begingroup\$ That can be a problem, as they share the same names and we can end up linking a mismatching set of objects. I guess it depends whether their public interfaces depend on NDEBUG etc. (when we add that to CFLAGS release builds). Instead of rewriting %.o: %c rules, I find it better to start a sub-make in the output directory (something like +$(MAKE) $(builddir) -f ../Makefile $@ VPATH=$(PWD)). Then you can build as many configurations as you want, from a single source directory which doesn't need to be writeable by the user doing the build. \$\endgroup\$ Commented Jun 27, 2024 at 12:10
  • 1
    \$\begingroup\$ Sorry for the very short review - I have a lot going on until late July. I hope you get a more thorough answer as well! \$\endgroup\$ Commented Jun 27, 2024 at 20:14
  • \$\begingroup\$ That's completely alright. Have a good month! \$\endgroup\$ Commented Jun 28, 2024 at 4:31

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.