My Makefile looks like this
opsh: shellparser.c main.c util.c errors.c
gcc -std=c99 -oopsh shellparser.c main.c util.c errors.c -lreadline -ltermcap
PREFIX = /usr/local
.PHONY: install
install: opsh
mkdir -p $(DESTDIR)$(PREFIX)/bin
cp $< $(DESTDIR)$(PREFIX)/bin/opsh
.PHONY: uninstall
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/opsh
The project is available on github. The statistics are
$ cloc .
30 text files.
30 unique files.
45 files ignored.
http://cloc.sourceforge.net v 1.60 T=0.08 s (283.8 files/s, 123084.0 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
C 6 837 2025 7105
C/C++ Header 7 53 15 143
Bourne Shell 7 6 6 93
yacc 1 8 2 56
CMake 1 12 0 28
make 1 3 0 10
YAML 1 1 0 5
-------------------------------------------------------------------------------
SUM: 24 920 2048 7440
-------------------------------------------------------------------------------
-
2\$\begingroup\$ Are you targeting GNU Make, or are you aiming for portability? \$\endgroup\$200_success– 200_success2017年08月05日 06:15:24 +00:00Commented Aug 5, 2017 at 6:15
-
2\$\begingroup\$ @200_success I feel that I must have portable code. I don't have enough experience to make the decision. I suppose I rather want portability. The project is a command-line interpreter (a custom Unix shell) and my goal is to learn autotools and how to package it for portability. \$\endgroup\$Niklas Rosencrantz– Niklas Rosencrantz2017年08月05日 06:20:22 +00:00Commented Aug 5, 2017 at 6:20
2 Answers 2
You should investigate whether GNU Make (or whichever Make implementation your users are supposed to use) has a special variable for all dependencies of the current rule, so that you don't need to list the source files twice.
The command line for gcc is missing the
-Wall -Wextra -O2options to catch common mistakes and to make the program run faster.If you want the Makefile to be portable to systems other than GNU Linux on x86_64, you should switch to GNU autoconf, but that's a lot of work. If your project is just a toy project, it's fine as it is.
Instead of specifying a particular compiler and flags, it's good practice to use Make's standard variables CC and CFLAGS, to allow users to pass in their preferences as necessary. Assuming that your source files are separately compilable, we usually separate compilation and linking, so that a change to shellparser.c doesn't require a rebuild of util.c and errors.c, for example. With those changes, the first two lines can be replaced with
CFLAGS += -std=c99
LDLIBS += -lreadline -ltermcap
opsh: shellparser.o main.o util.o errors.o
Built-in rules will do the rest.
If you have any header files, you'll want to make them dependencies of the object files they affect. You can do this manually, but a low-maintenance approach is to generate dependencies automatically. That's a whole question in itself, but since you're using GCC, you will probably want Combined Compilation and Dependency Generation:
DEPFLAGS = -MT $@ -MMD -MP -MF $*.Td
COMPILE.c = $(CC) $(DEPFLAGS) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
POSTCOMPILE = @mv -f $*.Td $*.d && touch $@
OBJECTS := shellparser.o main.o util.o errors.o
opsh: $(OBJECTS)
%.o : %.c
%.o : %.c %.d
$(COMPILE.c) $(OUTPUT_OPTION) $<
$(POSTCOMPILE)
%.d: ;
.PRECIOUS: %.d
include $(OBJECTS:.o=.d)
The install target would be better to use the install command, in order to set the correct ownership and permissions on the created files:
install: opsh
install -m755 -D -d $(DESTDIR)$(PREFIX)/bin
install -m755 $< $(DESTDIR)$(PREFIX)/bin/opsh
All Makefiles should have .DELETE_ON_ERROR, to cause the target to be removed if its recipe fails. Otherwise, a subsequent make would use in improperly-built target. The only case when you wouldn't want .DELETE_ON_ERROR is when debugging a compiler; it may be necessary to inspect the incomplete or invalid target file.
Modified Makefile
With the above changes, you have
OBJECTS := shellparser.o main.o util.o errors.o
opsh: $(OBJECTS)
CFLAGS += -std=c99
LDLIBS += -lreadline -ltermcap
DEPFLAGS = -MT $@ -MMD -MP -MF $*.Td
COMPILE.c = $(CC) $(DEPFLAGS) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
POSTCOMPILE = @mv -f $*.Td $*.d && touch $@
OBJECTS := shellparser.o main.o util.o errors.o
opsh: $(OBJECTS)
%.o : %.c
%.o : %.c %.d
$(COMPILE.c) $(OUTPUT_OPTION) $<
$(POSTCOMPILE)
%.d: ;
include $(OBJECTS:.o=.d)
.PRECIOUS: %.d
install: opsh
install -m755 -D -d $(DESTDIR)$(PREFIX)/bin
install -m755 $< $(DESTDIR)$(PREFIX)/bin/opsh
uninstall:
$(RM) $(DESTDIR)$(PREFIX)/bin/opsh
.PHONY: install uninstall
.DELETE_ON_ERROR