Gnu make exclusive OR (kinda) in prerequistires list? - c

I have a pattern rule like this:
$(BIN_DIR)/%: $(SOURCES_DIR)/%/*.c
$(CC) $? $(CFLAGS) $(LDFLAGS) -o $# $(LDLIBS) $(LIBS)
Is it possible to do something like:
$(BIN_DIR)/%: $(SOURCES_DIR)/%.c OR $(SOURCES_DIR)/%/*.c
$(CC) $? $(CFLAGS) $(LDFLAGS) -o $# $(LDLIBS) $(LIBS)
Where OR means if there the target $(BIN_DIR)/progname requsted, first look for $(SOURCEDS_DIR)/progname.c and it will be the only prerequisite. If there is no such file, prerequisites list will be $(SOURCES_DIR)/progname/*.c as it is now in the original rule? Maybe some other way to achieve this behaviour?
Full Makefile (tests ommited as irrelevant):
SOURCES_DIR = src
INCLUDE_DIR = $(SOURCES_DIR)/include
OBJECTS_DIR = build
BIN_DIR = bin
LIB_DIR = lib
INCLUDE_FLAGS = -I$(INCLUDE_DIR)
CFLAGS=-g --std=c99 -Wall -Wextra -O2 $(INCLUDE_FLAGS)
LDLIBS=-ldl -lm $(OPTLIBS)
LIB_SOURCES=$(wildcard $(SOURCES_DIR)/lib*)
BIN_SOURCES=$(filter-out $(INCLUDE_DIR) $(LIB_SOURCES),\
$(patsubst %.c,%,$(wildcard $(SOURCES_DIR)/*)))
BINS=$(addprefix $(BIN_DIR)/, $(notdir $(BIN_SOURCES)))
OBJECTS=$(patsubst %.c,%.o,\
$(subst $(SOURCES_DIR),$(OBJECTS_DIR),\
$(wildcard $(SOURCES_DIR)/lib**/*.c)))
LIBS=$(addsuffix .a, $(addprefix $(LIB_DIR)/, $(notdir $(LIB_SOURCES))))
SO_LIBS=$(patsubst %.a,%.so,$(LIBS))
all: $(OBJECTS) $(LIBS) $(SO_LIBS) $(BINS)
$(OBJECTS_DIR)/%.o: CFLAGS+= -fPIC
$(OBJECTS_DIR)/%.o: $(SOURCES_DIR)/%*
#mkdir -p $(#D)
$(CC) $(CFLAGS) -c $(SOURCES_DIR)/$*.c -o $# $(LDFLAGS) $(LDLIBS)
$(LIB_DIR)/%.a: $(OBJECTS)
$(AR) rcs $# $(OBJECTS_DIR)/$*/*.o
ranlib $#
$(LIB_DIR)/%.so: $(LIBS)
$(CC) -shared $(OBJECTS_DIR)/$*/*.o -o $# $(LDFLAGS) $(LDLIBS)
# the rule i'm talking about
$(BIN_DIR)/%: $(SOURCES_DIR)/%/*.c
$(CC) $? $(CFLAGS) $(LDFLAGS) -o $# $(LDLIBS) $(LIBS)

Confession: tl;dr
Is it possible to do something like:
$(BIN_DIR)/%: $(SOURCES_DIR)/%.c OR $(SOURCES_DIR)/%/*.c
$(CC) $? $(CFLAGS) $(LDFLAGS) -o $# $(LDLIBS) $(LIBS)
You can do it by declaring the pattern rule twice.
Put the recipe in a macro to avoid writing that twice.
define recipe
$(CC) $? $(CFLAGS) $(LDFLAGS) -o $# $(LDLIBS) $(LIBS)
endef
$(BIN_DIR)/%: ${SOURCES_DIR}/%.c
$(recipe)
$(BIN_DIR)/%: $(SOURCES_DIR)/%/*.c
$(recipe)
Ok, but don't do this.
Pattern rules are an ugly hack IMHO—I don't like the ambiguity.
Different make versions do different things.
(Wildcards aren't much better, but I won't comment further here.)
It always feels cleaner to write explicitly what you want.
You can still use patterns,
but use static pattern rules by explicitly listing the targets that the patterns apply to.
recipe = $(CC) $? $(CFLAGS) $(LDFLAGS) -o $# $(LDLIBS) $(LIBS)
single-source-targets := $(addprefix ${BIN_DIR}/,counter filter)# for example
multi-source-targets := $(addprefix ${BIN_DIR}/,engine database)
${single-source-targets}: ${BIN_DIR}/%: ${SOURCES_DIR}/%.c ; $(recipe)
${multi-source-targets}: ${BIN_DIR}/%: ${SOURCES_DIR}/%/*.c ; $(recipe)

Its impossible to give a specific answer without the entire Makefile.
But since this is a question of two variable file paths: conditional search directories ($(SOURCES_DIR) or $(SOURCES_DIR)/progname), AND conditional files to search for ($(SOURCES_DIR)/progname.c or $(SOURCEDS_DIR)/progname/*.c), the solution is more complicated than a simple makefile rule.
The best way I can think of is to use conditionals to set the dependency directory and filename as shown below.
ifneq ("$(wildcard $(SOURCES_DIR)/progname.c)","")
DEPS = $(SOURCES_DIR)/progname.c
else
DEPS = $(SOURCES_DIR)/progname/*.c
endif
$(BIN_DIR)/%: $(DEPS)
$(CC) $? $(CFLAGS) $(LDFLAGS) -o $# $(LDLIBS) $(LIBS)

Well, i got what i want, even though i don't really like how I did this, so any other variants are appritiated. It's difficult to build prerequisites list conditionally, so i decided to use different targets, and then just copy files where i wanted. This did the job, but still not very pretty. I just added following lines to the Makefile:
ONEFILERS_SOURCES=$(wildcard $(SOURCES_DIR)/*.c)
ONEFILERS=$(patsubst %.c,%,$(subst $(SOURCES_DIR),$(ONEFILERS_TMP),\
$(ONEFILERS_SOURCES)))
$(ONEFILERS_TMP)/%: $(SOURCES_DIR)/%.c $(SHARED_INCLUDES)
#mkdir -p $(#D)
$(CC) $< $(CFLAGS) $(LDFLAGS) -o $# $(LDLIBS) $(LIBS)
#install $# $(BIN_DIR)
Any better solutions are welcome.

Related

Makefile how to use logical OR between prerequisites?

My source files are organized nicely in a folder, that contains subfolders, that also contains source files.
The subfolders don't branch any further.
All object files are just meant to be created and stored in the same folder.
I don't want to manually list the name of the subdirs in the prerequisites when writing the target that generates my object files:
Right now this works:
$(OBJ)/%.o: $(SRC)/%.c
$(CC) -c $< -o $# $(CFLAGS)
$(OBJ)/%.o: $(SRC)/$(subdir1)/%.c
$(CC) -c $< -o $# $(CFLAGS)
$(OBJ)/%.o: $(SRC)/$(subdir2)/%.c
$(CC) -c $< -o $# $(CFLAGS)
...
But I want it to look something like this:
$(OBJ)/%.o: $(SRC)/%.c OR $(SRC)/*/%.c
$(CC) -c $< -o $# $(CFLAGS)
I understand that the title most likely isn't the real question to be asked, but I'm looking for any solution. Also, I know that the * doesn't work as a placeholder here.
First, you can simplify the makefile you have by using vpath:
$(OBJ)/%.o: %.c
$(CC) -c $< -o $# $(CFLAGS)
vpath %.c $(SRC) $(SRC)/$(subdir1) $(SRC)/$(subdir2)
Then, if you really don't want to specify the subdirectories:
vpath %.c $(shell find $(SRC) -type d)

passing target name to secondary targets

The makefile has multiple targets which will called for all files in the directory. The automatic variable does not get replaced for the target.
TARGETS += txyz_abc txyz_def
.PHONY: all clean
all: $(TARGETS)
txyz_abc: $(UY_DIR)/$(OBJ)/support.o \
$(UX_DIR)/$(OBJ)/%.o
$(CC) $(CFLAGS) -c $< -o $#
$(UX_DIR)/$(OBJ)/%.o: %.c
$(call mkdir, $(UX_DIR)/$(OBJ))
$(CC) $(CFLAGS) -c $< -o $#
$(UY_DIR)/$(OBJ)/support.o: $(UY_DIR)/src/support.c
$(call mkdir, $(UY_DIR)/$(OBJ))
$(CC) $(CFLAGS) -c -o $# $<
on make support.o is generated. but throws an error that no rule to make target '../ux_src/obj/%.o", needed by 'txyz_abc'.

Understanding Makefile syntax

Can someone help me understand the below makefile?
I have comment on the bits I am not sure on. I have used make files but not extensively and I do not believe I have followed good practises so any advice is welcome.
CC=gcc #is CC, libs, deps, obj, etc predefined keywords or could I use something else
CFLAGS=-I. -g -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse #same with CFlags
LIBS = -luuid -lfuse -pthread
DEPS = fs.h unqlite.h
OBJ = unqlite.o fs.o
TARGET1 = test
TARGET2 = test2
TARGET3 = test3
TARGET4 = test4
TARGET5 = main
all: $(TARGET1) $(TARGET2) $(TARGET3) $(TARGET4) $(TARGET5)
%.o: %.c $(DEPS) #not sure on this line
$(CC) -c -o $# $< $(CFLAGS) #same here
$(TARGET1): $(TARGET1).o $(OBJ)
gcc -o $# $^ $(CFLAGS) $(LIBS) #what are $# and $^
$(TARGET2): $(TARGET2).o $(OBJ)
gcc -o $# $^ $(CFLAGS) $(LIBS)
$(TARGET3): $(TARGET3).o $(OBJ)
gcc -o $# $^ $(CFLAGS) $(LIBS)
$(TARGET4): $(TARGET4).c
gcc -o test test.c
$(TARGET5): $(TARGET5).c
gcc -o uuid uuid.c -luuid
.PHONY: clean
clean:
rm -f *.o *~ core $(TARGET1) $(TARGET2) $(TARGET3) $(TARGET4) $(TARGET5)
CC, CFLAGS, LIBS, DEPS, OBJ and TARGETs are not predefined keywords. They are variables. You can change the name into any you feel appropriate. Just make sure you also change their reference names: $(CC) $(CFLAGS) etc.
%.o: %.c $(DEPS) -
It is a pattern rule: https://www.gnu.org/software/make/manual/html_node/Pattern-Rules.html
In brief, it says: any .o file depends on .c file with the same prefix and $(DEPS) (which are fs.h and unqlite.h)
$#, $<, $^ are automatic variables for the rules: https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html#Automatic-Variables
If works the following way: when making test.o object file from source, the rule
%.o: %.c $(DEPS)
$(CC) -c -o $# $< $(CFLAGS)
Is interpreted as:
test.o: test.c fs.h unqlite.h
gcc -c -o test.o test.c -I. -g -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse
Then, when making test binary, the rule
$(TARGET1): $(TARGET1).o $(OBJ)
gcc -o $# $^ $(CFLAGS) $(LIBS)
Becomes:
test: test.o unqlite.o fs.o
gcc -o test test.o unqlite.o fs.o -I. -g -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse -luuid -lfuse -pthread
So, we can see, CFLAGS reference is useless in the rule, as it defines compilation flags, and the rule actually performs linking. So the right one would be:
$(TARGET1): $(TARGET1).o $(OBJ)
gcc -o $# $^ $(LDFLAGS) $(LIBS)
Where LDFLAGS would be defined to some useful value, or can be left empty:
LDFLAGS =

Makefile doesn't rebuild objects on header modification

I have made a Makefile for compiling my C programm but it's not building object when i change one of the headers.
My MakeFile:
CC=gcc
CFLAGS=-O3 -Wall
LDFLAGS=-I/usr/include/mysql -L/usr/lib/x86_64-linux-gnu -lmysqlclient
SOURCES=$(wildcard *.c)
OBJECTS=$(SOURCES:.c=.o)
EXECUTABLE=bin/beta_parser
all: $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) -o $# $^ $(CFLAGS) $(LDFLAGS)
%.o:%.c types.h cstes.h headers.h mysql.h
$(CC) -o $# -c $< $(CFLAGS)
.PHONY: clean mrproper
clean:
rm -rf *.o
mrproper:
rm -rf $(EXEC)
What have I done wrong ?
EDIT : Corection of the Makeil after a great comment.
Although there are other more elegant tricks, in your case, I think something like
$(OBJECTS): types.h cstes.h headers.h mysql.h
%.o: %.c
$(CC) -o $# -c $< $(CFLAGS)
should be sufficient.
Scanning your sources for dependencies is outside the scope of Make (although there are other tools, such as CMake which will do this automatically). You need to add an explicit rule to generate these dependencies, but this can be done in many different ways. I've sometimes used the following technique:
OBJECTS = ....
-include $(OBJECTS:.o=.d)
...
$(OBJECTS): %.o: %.c
$(CC) $(CFLAGS) -c $< -o $#
$(CC) $(CFLAGS) $(DEPFLAGS) $< > $*.d
Google for "make automatic dependency generation" will show you other ways to do it as well.

GNU Make: different dependencies of several binaries in the same target?

First of all, I'm not familiar with GNU Make, so if I state some concept ridiculously wrong, please correct them instead of teasing me, thanks.
I want to have a default target that builds several executables with different dependencies, but I can't figure it out. Here is a minimal example I am using:
CC = gcc-4.8
CFLAGS = # some compiler flags
LDFLAGS = # some linker flags
SOURCES = prog1.c prog2.c
OBJECTS = $(SOURCES:.c=.o)
TARGET = prog1 prog2
$(TARGET) : $(OBJECTS)
$(CC) $(CFLAGS) -o prog1 prog1.o $(LDFLAGS)
$(CC) $(CFLAGS) -o prog2 prog2.o $(LDFLAGS)
.PHONY: clean
clean:
#rm -f $(TARGET) $(OBJECTS) core
But, as you can see, though prog2 is not at all related to prog1.c, editing prog1.c will result in the relinking of prog2. Is there any way to prevent this?
(Also, I would really appreciate it if someone can direct me to a good tutorial to GNU Make. The official documentation is huge...)
Each target has to have its own set of prerequisites; if you write:
foo bar: biz baz
that means "foo depends on biz and baz, and bar also depends on biz and baz". You want to write:
prog1: prog1.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $# $^ $(LDLIBS)
prog2: prog2.o
$(CC) $(CFLAGS) $(LDFLAGS) -o $# $^ $(LDLIBS)
Alternatively, you can write both targets in the same rule and define the prerequisites separately:
prog1 prog2:
$(CC) $(CFLAGS) $(LDFLAGS) -o $# $^ $(LDLIBS)
prog1: prog1.o
prog2: prog2.o
There are lots and lots of ways to make this more sophisticated and reduce typing. That's kind of what make is all about.
Update:
To build two targets at the same time by default, use
all: prog1 prog2
prog1 prog2:
$(CC) $(CFLAGS) $(LDFLAGS) -o $# $^ $(LDLIBS)
prog1: prog1.o
prog2: prog2.o
where all is the first target.

Resources